CPU Interrupted

Up until now, we have used a technique called polling to perform inputs from the Arduino‘s digital I/O pins. Polling consists in actively sampling the status of an external device or memory as a synchronous activity. On the Arduino, this usually occurs within the built-in loop() function. Polling is the method of choice in the majority of cases as it allows a program to easily and synchronously read external device states and values and handle external events. However, there are times when we want a program to handle an external event the moment it happens. This is what we explore in this post on interrupts.

Interrupts

In digital computers, a Central Processing Unit (CPU) sequentially executes instructions that manipulate data. This sequential execution of instructions can be interrupted by events to execute a different sequence of instructions, called an interrupt service routine, allowing the CPU to handle an event the moment it happens. Typically computers can handle interrupts from external sources such as timers, electrical signals, power failure, or events internal to the CPU such as accesses to invalid memory addresses, divisions by zero, and special interrupt instructions.

An interrupt is similar to a function call in that the current program counter is pushed onto the stack, thus remembering where to return upon completion of the interrupt service routine. Upon receiving an interrupt signal, the CPU completes the current instruction, disables interrupts, pushes the program counter onto the stack and calls the interrupt service routine. Upon executing a “return from interrupt” instruction at the end of the interrupt service routine, the program counter is popped from the stack, interrupts are enabled, and execution resumes where it had been interrupted in the program’s sequence of instructions. The difference between interrupt handling and a function call is that interrupt handling is disabled upon entering the interrupt service routine and that while function calls occur within the sequential flow of instructions, interrupts may occur anytime and anywhere within program execution. The fact that interrupts may occur anywhere in a program’s sequence of instructions may cause side effects which we will discuss later in this post.

Setting up Interrupt Service Routines

On the Arduino UNO, interrupt service routines can be written to handle events associated with external interrupt requests, pins, timers, Serial Peripheral Interface (SPI) data transfers, Inter-Integrated Circuit (I2C) data transfers, Universal Synchronous/Asynchronous Receiver/Transmitter (USART), Analog to Digital Converter (ADC), Electrically Erasable Programmable Read Only Memory (EEPROM) and flash memory accesses. In this post, we will only deal with external interrupts using the Arduino built-in function attachInterrupt(). On the Arduino Uno, external interrupts are tied to levels and state changes for digital input pins 2 and 3. Interrupts may be set to occur when the state of the input pin changes, on the rising or falling edge of the signal, or when the input pin is LOW. The attachInterrupt() function is used as follows:

attachInterrupt (interrupt, ISR, mode)

Where interrupt is the number of the interrupt. On the Arduino Uno, interrupt number 0 corresponds to external interrupts from digital pin 2 and interrupt number 1, corresponds to external interrupts from digital pin 3. Interrupt numbers may differ between Arduino board types. For this reason, it is recommended not to use the interrupt number directly, but to use the digitalPinToInterrupt() built-in function that returns the interrupt number associated to the pin. ISR (Interrupt Service Routine) is the name of the function to be called when an interrupt occurs. This function takes no argument and does not return a value. Finally, mode, specifies the mode of operation for the external interrupt. The following values are supported for the mode argument:

  • LOW – Interrupt when the pin is LOW.
  • CHANGE – Interrupt when the pin changes from LOW to HIGH or HIGH to LOW.
  • RISING – Interrupt when the pin changes from LOW to HIGH.
  • FALLING – Interrupt when the pin changes from HIGH to LOW.

Typically, the attachInterrupt() function is called in the built-in setup() function, where we set the pin mode of digital input 2 or 3 to INPUT or INPUT_PULLUP, and attach an interrupt service routine to one of the two pins. For instance, in the following code snippet:

// Switch value will be read from digital input pin 2
define INPORT 2

// Interrupt Service Routine
void buttonPress() {
  // Handle button press
}

// Setup the board.
void setup() {
  // Set Arduino's input ports
  pinMode(INPORT, INPUT);

  //Initialize interrupt service routine
  attachInterrupt(digitalPinToInterrupt(INPORT), buttonPress, FALLING); 
}

We declare a function, buttonPress(), that takes no argument and does not return a value. This function is called when an interrupt occurs. Within the built-in setup() function, we set the mode of digital pin 2 to INPUT using the built-in pinMode() function. We then attach interrupt service routine buttonPress() to digital pin 2. The interrupt service routine is called when the value at the input pin goes from HIGH to LOW, from 5 volts to 0 volts. The built-in function attachInterrupt() is used to attach the interrupt service routine buttonPress() to the digital pin. Note the use of the built-in function digitalPinToInterrupt() to make the use of digital pin 2 independent of board type.

Interrupt Side-Effects

Since interrupt service routines take no arguments and do not return information, they can only exchange information with the rest of the program through global variables. Global variables are variables declared in the program’s header, outside of functions, that are accessible at all times by all parts of the program. We have used global variables already to hold information shared between the setup() and loop() built-in functions and to hold information persistent between iterations of the loop() built-in function.

Side-effects arise because interrupt service routines use and modify information that is also used and modified by the main program. Because interrupts may occur anytime while a program is running, the interrupt service routine may modify information while the main program instructions were in the midst of looking at or even modifying the same information. In the next sections, we will look at two types of side effects that occur within a micro-controller when using interrupts. The first type of side-effect is volatility caused by the use of a high-level programming language when dealing with interrupts. The second type of side-effect is concurrent data access that cause data manipulations to be erroneous in certain conditions.

Volatility

When we write code for the Arduino, we do so in a programming language called C++ which is translated into machine code, real instructions for the Atmega processor on the Arduino Uno board, by a compiler. When it translates C++ code into machine code, the compiler makes assumptions and optimizes the final code as much as it can. It may decide, for instance, never to store a variable in memory and use the processor’s storage registers instead, or it may decide to delay storing the content of a variable in memory until the end of a routine. This may be problematic for variables that are to be used by an interrupt service routine as well as in the main program. The interrupt service routine may start using a variable by loading it from memory while the main program was manipulating the same variable temporarily held in one of the processor’s a storage register.

The C++ language supports a directive called volatile which directs the compiler to always use the variable from main memory and not from a storage register. A variable should be declared volatile if its value can be changed outside the scope and control of the code section in which it appears. This is the case of all variables that are manipulated by the interrupt service routine as well as in the rest of the program’s code. the volatile directive is used within the declaration of a variable and generally precedes the variable type declaration as in the following variable declaration:

volatile int counter = 0;

Variable counter is declared as a volatile signed integer (int) initialized with the value 0. Its value can be changed by the main program as well as within an interrupt service routine directly in main memory.

Concurrent Data Access

Errors due to concurrent data access occur when, upon an interrupt, the interrupt service routine uses or modifies global variables while code in the main program uses or modifies the same global variables. Several things can go wrong, among which: the main program reads the global variable, the interrupt service routine stores a new value for the global variable, then the main program overwrites the global variable, causing the interrupt routine to have no effect; the main program updates the global variable with an intermediate computation value, then the interrupt service routine erroneously uses the intermediate value; the main program uses the global variable to make a sequence of operations and in the midst of the sequence, the interrupt service routine modifies the global variable causing errors in the main program sequence.

All of these errors are caused by the fact that a value was used or modified in mid-stride by two parts of the same program. The way to fix these problems is to use and modify values atomically, which is to say in an indivisible way. For instance, if a value used in both the main program and in an interrupt service routine is to be read, modified, and saved, then all of these operations must occur as if done by a single uninterruptible instruction. The way to prevent interruption while a series of instructions is being executed is to disable interrupts while the value is used and then to enable interrupts again. To do this on the Arduino, we use the built-in functions noInterrupts() and interrupts(). Hence, in the following code sequence slightly modified from the previous post:

noInterrupts();
digitalWrite(LED0, litLED == 0);
digitalWrite(LED1, litLED == 1);
digitalWrite(LED2, litLED == 2);
digitalWrite(LED3, litLED == 3);
interrupts();

If we were not to disable interrupts while variable litLED is used, we would run in the possibility that an interrupt service routine modifies the value of litLED between calls to built-in function digitalWrite(). To illustrate the problem better, let’s imagine that interrupts are not disabled and that the value of litLED is 1. The first two digitalWrite() function calls are executed resulting in LED0 being extinguished and LED1 being lit. An interrupt occurs and the interrupt service routine increments the value of litLED by one, then returns. Execution resumes and the third digitalWrite() function call lights LED2 since the value of litLED is now 2. The end result is that LEDs 1 and 2 are simultaneously lit while the intent was to always light only one LED in sequence. Disabling interrupts while the sequence of digitalWrite() function calls execute resolves the problem. It is important to note from this example that the whole sequence must be executed with interrupts disabled because all four LEDs as a whole represent the value of variable litLED. If an interrupt does occur while interrupts are disabled, it is processed as soon as interrupts are re-enabled, right after the call to the interrupts() built-in function.

Putting it Together

In order to demonstrate the use of external interrupts on the Arduino, we will use the same circuit as was used in the previous post, Switch Debouncing. I built a circuit and Arduino program that sequentially turns LEDs on and off once a second as well as every time a high to low edge is detected on digital pin 2 of an Arduino Uno. A push-button is attached to digital pin 2 and we will use an interrupt service routine to react to the push-button being depressed.

The Electronics Setup

In this circuit, the Arduino’s digital input/output pin 2 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.

CPU Interruption Demonstration Sketch

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

/*
Switch Interrupt sketch
Uses four LEDs connected to digital I/O pins 8, 9, 10, and 11 to demonstrate
how interrupts work. The LEDs are lit one after the other, once a second, as
well as when the switch is depressed. When the switch is depressed, it causes
an interrupt that calls an interrupt routine that increments the count and
then displays the LED corresponding to the count.
MIT License
Copyright (c) 2020, Michel Lagace
*/

// Delay time to wait at end of each loop
#define WAIT_TIME 1000

// Switch value will be read from pin 2
#define INPORT 2

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

// Increment count
void countUp() {
  counter++;
  if (counter > 3) {
    counter = 0;
  }
}

// Display count
void displayCount() {
  // Light up the appropriate LED
  digitalWrite(LED0, counter == 0);
  digitalWrite(LED1, counter == 1);
  digitalWrite(LED2, counter == 2);
  digitalWrite(LED3, counter == 3);
}

// Interrupt Service Routine
void buttonPress() {
  countUp();
  displayCount();
}

// 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 currently lit LEDs
  counter = 0;

  //Initialize interrupt routine
  attachInterrupt(digitalPinToInterrupt(INPORT), buttonPress, FALLING);
}

// Repeat forever
void loop() {
  // Count up and display count
  noInterrupts();
  countUp();
  displayCount();
  interrupts();

  // Wait for a while
  delay(WAIT_TIME);
}

In the sketch above, we first define WAIT_TIME, the time to wait before lighting the next LED in the sequence, then INPORT, digital input port 2 from which the program will get interrupts, the four digital output ports attached to the LEDs to light in sequence, and finally, counter, the volatile integer variable maintaining which LED to light. Global variable counter is set to volatile because, as discussed previously, it is used and modified in the interrupt service routine as well as in the main program.

Next, we define two functions that are used throughout the program. Both functions do not take arguments and do not return values. Function countUp() increments global variable counter and keeps its value between 0 and 3. The other function, displayCount(), lights the LED corresponding to the value of counter and extinguishes the other LEDs.

The interrupt service routine buttonPress() is then defined. It is called by the Arduino internal software whenever the push-button is pressed. We will see later how the interrupt service routine is declared to the Arduino system. The buttonPress() routine increments the counter global variable using the countUp() function and then lights the appropriate LED using the displayCount() function. Note that interrupts are already disabled when entering the interrupt service routine which guarantees that all operations within the interrupt service routine are executed atomically, without interruption.

The setup() built-in function, called once before the loop() built-in function is repeatedly called, sets digital input/output port INPORT for INPUT and digital input/output ports LED0, LED1, LED2, and LED3 for OUTPUT. It then initializes the counter global variable to zero. No need to disable interrupts at this point since the interrupt service routine has not been set yet. Next, we declare the buttonPress() interrupt service routine through the attachInterrupt() built-in function. The digitalPinToInterrupt() built-in function is used to map INPORT to the internal interrupt number used by the system. Digital input port INPORT is set to interrupt and call the buttonPress() interrupt service routine when the signal at the INPORT digital input pin goes from HIGH to LOW by using the FALLING parameter.

Finally, the loop() built-in function is repeatedly called. It increments the counter global variable using the countUp() function and then lights the LED associated to the value of counter using the displayCount() function. Both countUp() and displayCount() functions are sandwiched between calls to built-in functions noInterrupts() and interrupts() ensuring that manipulations of the counter global variable are done atomically and that no interrupts occur while the global variable is acted upon. The delay() built-in function is then called to make the program wait the specified amount of time.

Demonstration

Set up the circuit as shown previously and, using the Arduino IDE, compile and download the code onto the Arduino board. As the code starts executing, the LEDs will light in sequence, one after the other, every second. Every time the switch is depressed, an interrupt is generated and the buttonPress() interrupt service routine gets called, which increments the counter and lights the next LED in the sequence, thus demonstrating the use of interrupts on an Arduino board.

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.

Morse Code Reader

In a previous post, I have shown how to send Morse code. Wouldn’t it be nice if we could read it and convert it back to text? This is what I intend to demonstrate in this blog post. We will see how to program the Arduino to read from a digital input pin and look at how to convert digital on/off information into Morse code sequences of dots and dashes, and then into text. The featured image at the beginning of this blog post is Copyright Museums Victoria / CC BY. The image was downloaded from Museums Victoria.

As mentioned in my previous post, Morse Code Generator, Morse code is a method of transmitting information as a series of on-off tones, lights, or clicks. Basically, letters, numbers, and punctuation marks are translated to a variable length collection of dots and dashes, of shorter and longer bursts of sound or light. The duration of the dot, the short burst, is the unit of time by which all other elements of Morse code are defined. A dash, the long burst, is a signal whose duration is three times that of the dot. The time between dots and dashes within an encoded character is one unit of time. the time between characters is three units of time and the time between words is seven units of time.

International Morse code is thus composed of five elements:

  • short mark, dot or ‘dit’: one time unit long
  • longer mark, dash or ‘dah’: three time units long
  • gap between the dots and dashes within a character: one time unit long
  • gap between letters of a word: three time units long
  • gap between words: seven time units long

Following, is the international Morse code equivalent, in dot (.) and dash (-) notation for each alphabetical and numerical character:

315px-International_Morse_Code.svg

A Question of Timing

Decoding Morse code is all about measuring time. Measuring the amount of time the tone, light or electrical signal is on and the amount of time the tone, light, or electrical signal is off. Take the following diagram depicting the Morse code signal for the text “A TEST”.

Morse Code Timing

It is the on signal time that determines whether a dot or dash was transmitted. As shown above, whenever the signal is on for one unit of time, a dot (or dit) was transmitted and whenever the signal is on for three units of time, a dash (or dah) was transmitted. The off signal time determines if we are within a character being transmitted, if we are between characters, if we are between words, or if we are at the end of a transmission. If the signal is off for one unit of time, we are within a character, if the signal is off for three units of time, we are between characters of a word, if the signal is off for seven units of time, we are between words, and if the signal is off for more than seven units of time, then the transmission is finished.

Measuring Time

In order to measure for how long the signal stays on or off, we must first determine when a signal starts to be on and when the signal starts to be off. In a previous blog post, LED Toggle with a Push-Button Switch, I introduced a piece of code that detects when a signal goes from the off to the on state or from the on to the off state. This piece of code is called an edge detector because it detects the moment the signal changes state, that is the moment an edge (vertical line) occurs in the digital signal. The piece of code introduced was the following:

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

In the code, we first read the actual value of the signal, then read its value until it changes. Once the value changes, we wait a bit to let the signal settle. The function then returns the last value read. This works except for one thing: we need to detect end-of transmission, which occurs if a low signal lasts for significantly more than seven time units. We need to add a timeout to the logic and try to detect a state change, but only for a certain amount of time. Also, because we need to detect a state change from a previously known state, we want to pass the previous state to the function instead of doing an initial signal read in the function. This will allow for a state change occurring outside the function.

Following is the new detectEdge() function. The first modification to the edge detection function is that three parameters were added to the function: the Arduino digital input port to read from, a timeout value in milliseconds, and a Boolean value, startingState, containing the current state of the signal, on or off. At the beginning of the function body, we declare Boolean variable signalState that will be used to hold the current signal state. We then get the current process time in milliseconds.

Note that signalState is initialized to the startingState value, ensuring that signalState is always valid. It is a good practice to initialize variables upon their declaration as it prevents them from having unknown values. In the following code, if signalState was not initialized, it would be possible to exit the while loop upon a timeout, without ever assigning a value to signalState causing the function to return an unitialized value that may randomly take any value.

// Wait for an edge or timeout.
bool detectEdge(int port, int timeout, bool startingState) {
  unsigned long timerStart = millis();
  bool signalState = startingState;
  while (true) {
    if ((millis() - timerStart) > timeout) {
      break;
    }
    signalState = digitalRead(INPORT);
    if (signalState != startingState) {
      break;
    }
  }
  return signalState;
}

A while(true) infinite loop follows. Within the loop, we first check if we have timed-out and if so, exit the loop. If not, we read the signal value from the input port and if the signal value is different from the starting state, we also exit the loop. The loop continues indefinitely until either a timeout occurs or a signal value change is detected. The current signal state value is returned at the end of the function.

Evaluating Morse Code Time Units

Now that we are detecting edges, we can measure the time a signal remains on or off, true or false. We need to determine if a portion of the signal lasted for 1, 3, or 7 units of time or longer. We could simply take the elapsed time between edges and divide it by the time 1 unit of time takes. This would give us the exact number of time units a portion of the signal lasted, which could be, say, 0.9, 3.1, or 6.8. Intuitively, we can see that 0.9 time units is really 1 time unit, 3.1 is 3 time units, and 6.8 time units is 7 time units. When measuring signal time, we have to allow for a bit of error in the measurement of time or in the signal production. After all, Morse code can be manually produced by a key operator and speed will vary with time.

A solution to this problem could be to round the measured time unit to the nearest integer. Hence, 0.5 to 1.49 time units would be considered to be 1 time unit and 2.5 to 3.49 time units would be considered 3 time units. Now what would we do if we had some portions of signal that were measured to last 2, 4, 5, 6, 8, or 9 time units in this manner? Would we generate an error? Again, intuitively, 4 units of time is a lot closer to 3 units of time than it is to 7 units of time and 6 units of time is closer to 7 units of time. We are less interested in in-between values than we are in deciding if a portion of signal is closer to 1, 3, or 7 units of time, or if it is completely off the scale.

Since we want the number of units of time to be either 1, 3, or 7, we need to define ranges that translate into these values. Let’s use, as a first rule, that the time a portion of signal lasts must be within 50 percent of its nominal value in time units. That is a 1 time unit signal must be between 0.5 and 1.5 time units. For any portion of a measured signal, the measured time T, in time units, must then be within the ranges

1 time unit: 0.5 ≤ T < 1.5
3 time units: 1.5 ≤ T < 4.5
7 time units: 3.5 ≤ T < 10.5

Using this rule, all time measurements that are within 0.5 and 10.5 time units are covered. There is an overlap between 3.5 and 4.5 time units, however. This can be resolved by splitting the difference and specifying the following ranges

1 time unit: 0.5 ≤ T < 1.5
3 time units: 1.5 ≤ T < 4
7 time units: 4 ≤ T < 10.5

Anything below 0.5 time units is thus considered as if there was no signal, or as if there was noise on the line; anything above 10.5 time units is considered a time out, or an end of transmission. The following code translates an elapsed time in Morse code time units according to the rules just stated.

// Convert time in milliseconds to Morse code time units
int timeUnits(int elapsedTime, int timeUnit, int timeout) {
  int value = 15;
  if (elapsedTime < 0.5*timeUnit) {
    value = 0;
  }
  else if (elapsedTime < 1.5*timeUnit) {
    value = 1;
  }
  else if (elapsedTime < 4*timeUnit) {
    value = 3;
  }
  else if (elapsedTime < timeout) {
    value = 7;
  }
  return value;
}

The function timeUnits() takes three parameters: the elapsed time in milliseconds to be converted to Morse code time units; the number of milliseconds corresponding to one Morse code unit of time; and the number of milliseconds corresponding to a timeout. This last parameter could have been set to 10.5 time units in the code, but this timeout value is also used when detecting edges and in order to ensure that values are coherent throughout the code, it is set once and passed as a parameter.

The body of the function starts by assuming that there will be a timeout and sets the number of time units to 15, arbitrarily chosen to represent a timeout. Then the unit time value is set according to where the number of elapsed milliseconds fall within the range of acceptable values. Finally, the function returns the value of time units, either 0, 1, 3, 7, or 15.

Sending Text Back to the PC

Before continuing with Morse code signal analysis and decoding, let’s have a look at a very useful class in the Arduino library: the Serial class. It is used to allow the Arduino board to communicate with a computer or other devices. All Arduino boards have at least one serial port that communicate on digital pins 0 and 1 as well as with a computer through the board’s USB connector. You cannot use pins 0 and 1 for input/output and serial communication at the same time.

The Arduino Serial class supports several methods to configure, send and receive information from the serial port. In this project, I made use of three of these methods:

  • The begin() method is used to initialize the serial port with serial communication speed, called a baud rate, and communication options including the number of data bits, from 5 to 8; the parity type, odd, even, or none; and the number of stop bits.
  • The print() method sends ASCII characters corresponding to the method argument. If the argument is a String, the characters of the string are sent as is. If the argument is an integer or a floating-point number, the numerical value is first converted to a string and then sent as ASCII characters.
  • The println() method behaves the same as the print() method, but appends a carriage return character (ASCII 13 or ‘\r’) and a new line character (ASCII 10 or ‘\n’) to the information sent.

Converting Morse Code to Text

Now that we measure time and are evaluating the number of time units the signal is on or off, we can convert the signal to Morse code and then to text. First, we declare global information used throughout the code. In the following code snippet, we set the Morse code signal to be read from the Arduino’s board pin 8, INPORT. We set the unit of time, TIME_UNIT, to 50 milliseconds, corresponding to the CODEX speed standard of 20 Words Per Minute (WPM), and we set the timeout value, READ_TIMEOUT, at 10.5 times unit of time. There are three global variables used to hold information between calls to the loop() function: currentCode holds a Morse code character being read, currentLine holds the text string being decoded from Morse code, and currentState holds the current state of the signal being decoded.

/*
 Morse Code Reader
 Program that reads Morse code and outputs decoded information to the
 Serial interface. It is associated with the Morse Code decoder blog
 post on https://lagacemichel.com
 MIT License
 Copyright (c) 2019, Michel Lagace
 */

 define INPORT 8
 define TIME_UNIT 50
 define READ_TIMEOUT 10.5*TIME_UNIT
 String currentCode;
 String currentLine;
 bool currentState;

The next code snippet contains the setup() function that initializes the digital input pin to receive the Morse code signal, initializes global variables, and sets up the serial communication to communicate with the PC at 9600 baud, or approximately 10 characters per second.

// Setup the board.
 void setup() {
   pinMode(INPORT, INPUT);
   currentState = false;
   currentCode = "";
   currentLine = "";
   Serial.begin(9600);
 }

The decodeSequence() function converts a series of dots and dashes representing the signal sequence of a Morse code character into the equivalent ASCII character. In the code below, we first initialize a String, characters, that contains the collection of all supported characters, from ‘a’ to ‘z’ and from ‘0’ to ‘9’. We then have an array of String objects, codedCharacters, that contains the Morse code corresponding to each character in the first String. The decodeSequence() function takes one parameter, sequence, a String containing the Morse code to decode. The function returns a single ASCII character corresponding to the decoded Morse code signal. It starts by initializing character, the variable containing the decoded Morse code to an asterisk. This is the character returned if the Morse code cannot be found in codedCharacters. It loops through the list of Morse codes in codedCharacters comparing each one to the sequence to be decoded. If found, the index of the sequence found is used to identify the character in characters. The character found or an asterisk is returned at the end of the function.

// List of valid alphanumeric characters
static String characters = "abcdefghijklmnopqrstuvwxyz0123456789";

// Morse code sequences for each alphanumeric character
static String codedCharacters[] = { 
   ".-", "-…", "-.-.", "-..", ".", "..-.", "--.", "….",
   "..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.",
   "--.-", ".-.", "…", "-", "..-", "…-", ".--", "-..-",
   "-.--", "--..", "-----", ".----", "..---", "…--",
   "….-", "…..", "-….", "--…", "---..", "----."};

 // Convert Morse code signal to a character
 char decodeSequence(String sequence) {
   int i;
   char character = '*';
   for (i = 0; i < sizeof(codedCharacters)/sizeof(String); i++) {
     if (codedCharacters[i] == sequence) {
       character = characters[i];
       break;
     }
   }
   return character;
 }

The Main Loop

The following piece of code contains the loop() function of the program. It waits for a signal’s edge, accumulates Morse code dashes and dots, decodes the dots and dashes into characters and assembles the characters into words to form a sentence. The loop() function is split into three sections: the edge detection section, the falling edge processing section and the rising edge processing section.

// This is the main loop. It monitors INPORT digital IO
// pin for Morse code, decodes the signal and sends decoded
// text to the attached PC.
void loop() {
  // Wait for a rising edge, falling edge, or time out
  unsigned long startTime = millis();
  bool previousState = currentState;
  currentState = detectEdge(INPORT, READ_TIMEOUT, previousState);
  int elapsedTimeUnits =
    timeUnits(millis() - startTime, TIME_UNIT, READ_TIMEOUT);

  // On a falling edge, append dot or dash to character
  if (previousState && !currentState) {
      if (elapsedTimeUnits == 1) {
        currentCode += ".";
      }
      else if (elapsedTimeUnits == 3) {
        currentCode += "-";
      }
  }

  // On a rising edge or time out, handle end of
  // character, word, or end of transmission. 
  else if (currentCode != "") {
    if (elapsedTimeUnits > 1) {
       currentLine += decodeSequence(currentCode);
      currentCode = "";
    }
    if (elapsedTimeUnits > 3) {
      currentLine += ' ';
    }
    if (elapsedTimeUnits > 7) {
      Serial.println(currentLine);
      currentLine = "";
    }
  }
}

In the edge detection section, we save the current time since the program started in milliseconds using the millis() function provided by the system. We then call the detectEdge() function which returns the current signal state after detecting an edge or a timeout. We measure the elapsed time in milliseconds by subtracting the current time since the program started from the time saved before detecting the edge. The time in milliseconds is converted to Morse code time units through a call to the timeUnits() function.

In the second section, if the previous signal state was on and the new state is off, we have a falling edge. Any other signal condition is either a rising edge or a timeout. In the case of a falling edge, if the on signal lasted for 1 time unit, add a ‘dot’ to currentCode. If the on signal lasted for 3 units of time, add a ‘dash’ to currentCode. Do not do anything if the signal lasted for any other amount of time.

In the third section, we process information only if some dots and dashes have been accumulated in the currentCode variable. If the off signal lasted 1 unit of time or less, do nothing and continue accumulating dots and dashes in the currentCode variable. If the off signal lasted for more than 1 time unit, decode the dots and dashes sequence into its corresponding character and add it to the sentence being formed; reset the sequence of dots and dashes. In addition, if the off signal lasted more than 3 time units, add a space character to the sentence. Finally, if the off signal lasted more than 7 time units, return the decoded sentence to the serial line through a call to Serial.println() and reset the current line.

Putting it All Together

Let’s use the same setup as was used in my previous post Raspberry Pi Speaks Arduino. Connect the different parts using a solderless breadboard, jump wires, a BC337-40 transistor, and a 10K, a 220K, and a 150K resistor as depicted. Through one of the breadboard’s ground rail, we connect the Raspberry Pi and Arduino’s ground pins. The Raspberry Pi’s 3.3-volt supply is connected to the transistor’s base through the 220K resistor. The Raspberry Pi’s GPIO4 output is connected directly to the transistor’s emitter. The Arduino’s 5-volt supply is connected to the transistor’s collector through a 10K resistor. The Arduino’s digital input 8 is connected directly to the transistor’s collector.

Get the Python and Arduino code for the Morse Code Reader demonstration by accessing the MorseCodeReader repository on Github at https://github.com/lagacemichel/MorseCodeReader and download a copy of MorseCodeTutorial.py on the Raspberry Pi and a copy of MorseDecodeTutorial.ino on your PC. On the PC, using the Arduino IDE, load the MorseDecodeTutorial.ino file and load it on the Arduino board. On the Raspberry Pi, run one of the Python 3 IDEs, load the MorseCodeTutorial.py file and run it.

On the PC, within the Arduino IDE, press the control, shift, and M keys simultaneously. This will make the Serial Monitor window appear. As the Python program on the Raspberry Pi sends Morse code signals, the Arduino board decodes it until an end of transmission is detected, then it sends the text, through the serial port, back to the PC. The text is displayed on the Serial Monitor window and the output should look like the following:

We have just demonstrated Morse code sent by a Raspberry Pi Python program, received by an Arduino digital input pin through a level shifting transistor, decoded back to text within the Arduino and sent to the PC for display through the serial communication port.

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.

Morse Code Generator

Lets Beef-Up the ‘Blink’ Program

A blinking circuit may be useful as a signal to warn others of dangerous situations or to draw people’s attention. Another use for a blinking light is to communicate with someone over a distance. The following program makes use of the blinking light circuit to implement a Morse code generator.

Around 1837, the American Samuel F. B. Morse invented an early version of what was to become Morse code. Morse code is a method of transmitting information as a series of on-off tones, lights, or clicks. Basically, letters, numbers, and punctuation marks are translated to a variable length collection of dots and dashes, of shorter and longer bursts of sound or light. The duration of the dot, the short burst, is the unit of time by which all other elements of Morse code are defined. A dash, the long burst, is a signal whose duration is three times that of the dot. The time between dots and dashes within an encoded character is one unit of time. the time between characters is three units of time and the time between words is seven units of time.

International Morse code is thus composed of five elements:

  • short mark, dot or ‘dit’: one time unit long
  • longer mark, dash or ‘dah’: three time units long
  • gap between the dots and dashes within a character: one time unit long
  • gap between letters of a word: three time units long
  • gap between words: seven time units long

Following, is the international Morse code equivalent, in dot (.) and dash (-) notation for each alphabetical and numerical character:

315px-International_Morse_Code.svg

For instance, if we are to show the character sequence “PARIS” in Morse code, we can represent it in text as “.–.  .-  .-.  ..  …” and if we want to represent it as a series of on and off signals, arbitrarily represented by an equal sign “=” and an underscore “_” respectively, each one time unit in duration and we follow the rules stated above, we get the following signal:

___=_===_===_=___=_===___=_===_=___=_=___=_=_=___

The following program, written in “C” for the Arduino, converts a character string of alphabetical characters into a series of short and long blinking LED signals corresponding to their morse code equivalent. You can reconstruct the program in the Arduino IDE (Integrated Development Environment) by copying the code from this post and pasting it in sequence, as it appears in this text, in the text editing pane of the IDE.

The Program Header

The program header, a few paragraphs down, contains a comment describing the nature and purpose of the program as well as a copyright notice. I am the author of the code and I have put it in the public domain. Comments are character sequences ignored by the program interpreter or compiler. Comments can be formatted as any text between a slash-asterisk, ‘/*‘, and asterisk-slash, ‘*/‘, character sequences. This is the original way to enter comments in the ‘C’ language. Text enclosed between these two-character sequences can span several lines and are ignored. Comments can also be entered after a double-slash, ‘//‘, character sequence. All text after the double-slash character sequence, until the end of line, is considered a comment and is ignored by the program interpreter or compiler.

We define the digital output PORT as LED_BUILTIN, the digital output port corresponding to the Arduino’s built-in LED. The UNIT_TIME value corresponds to the time in milliseconds of the ‘on’ duration of the dot. The #define statement allows programmers to associate text to a name. When the program is compiled, the text replaces the name whenever it appears in the program. At compile time, PORT is replaced by the Arduino LED_BUILTIN  constant and UNIT_TIME is replaced by the integer value 100. Note that LED_BUILTIN is also a definition and it gets substituted by the appropriate integer value depending on the target Arduino board.

The characters String variable and codedCharacters String array variable work in tandem as the list of supported characters and their corresponding Morse code equivalent. The Morse code equivalent is encoded as a character string containing a series of period (‘.‘) and dashes (‘‘) representing the short and long bursts of Morse code. Each string in the codedCharacters array corresponds to the character at the same index in the characters string variable.

/* Morse Code Generator
   Blink an LED connected to pin LED_BUILTIN to display morse
   code corresponding to text entered. Repeats forever.
   This sketch was written by Michel Lagacé, 2018-09-02
   This code is in the public domain */

// Output port to display morse code
#define PORT LED_BUILTIN

// unit time length of the morse encoding
#define UNIT_TIME 100

// Characters to be encoded
static String characters = "abcdefghijklmnopqrstuvwxyz";

// Morse code sequences for each character
static String codedCharacters[] = { 
    ".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....",
    "..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.",
    "--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-",
    "-.--", "--.." };

In ‘C++‘, an array is a collection of elements accessible through the use of an index. A String is a specialized array for collections of characters. Thus, each character in a String and each element of an array can be accessed through the use of an index. An index is an integer value enclosed in square brackets ([ ]) right after the variable name. The first character of a string or element in an array has index 0. The second character or element has index 1 and each successive character or element’s index is one more than the preceding index.

In the code above, character ‘c’ is at index 2 in the characters String. The corresponding Morse code sequence, “-.-.”, is a String at index 2 in the codeCharacters String array. The characters variable is declared as static meaning that the variable’s memory is allocated once at the start of the program and remains for the duration of the program. Non-static variables in functions are allocated in memory only for the duration of the execution of the function, then released back to the pool of memory a program can use. This is very useful for very large programs as only the necessary memory is used at any time. The characters String is initialized with the character string “abcdefghijklmnopqrstuvwxyz”. codedCharacters is also declared as a static String, but the variable name is followed by square brackets. These square brackets indicate that the variable is an array. The size of the array is set to the number of elements it is initialized with. In this case, 26 elements. If an uninitialized array is to be created, the size can be specified as a value within the square brackets.

Digital Output Setup

The setup of the digital output to blink an LED is identical to the setup in the ‘BLINK’ circuit demonstration. We set the pin mode to OUTPUT.

// Setup the board. Digital port LED_BUILTIN in output mode
void setup() {
    pinMode(PORT, OUTPUT);
}

Outputting Dots and Dashes

Code can be, and should be, segregated into functions that provide specific well-defined functionality to a program. Functions, also called subroutines, are sequences of code that can be called by name. In the ‘C’ language, functions must be defined before they are used and thus must appear in the code before any code that uses them. The simplest functions have the following structure:

void functionName() {
    // Sequence of code the function performs when called
}

The void reserved word specifies that the function does not return any value. The we have the function name, followed by a sequence of code enclosed in curly braces, ‘{‘ and ‘}‘. Elsewhere in the program, if we want to call up the sequence of code contained i the function, we simply use the function name, followed by parentheses and a semicolon:

    functionName();

In the Morse code program, we first define two functions, one to output a dot, put the LED on for one time unit, then off for one time unit; and the other to output a dash, put the LED on for three time units, then off for one time unit. In the following code, you will find both functions. They are very similar to the code found in the original ‘BLINK’ program: lights on, lights off.

/* Function to output a dot: one unit on, one unit off */
void outputDot() {
    digitalWrite(PORT,HIGH);
    delay(UNIT_TIME);
    digitalWrite(PORT,LOW);
    delay(UNIT_TIME);
}

/* Function to output a dash: three units on, one unit off */
void outputDash() {
    digitalWrite(PORT,HIGH);
    delay(UNIT_TIME*3);
    digitalWrite(PORT,LOW);
    delay(UNIT_TIME);
}

Encoding a Character in Morse Code

Next, we have a function that outputs a character as a series of dots and dashes. Before having a look at this function, let’s go through a few concepts that will help people unfamiliar with ‘C++’ understand the code better.

‘C++’ Classes

In ‘C++’, functions can be attached to data structures called classes. I will not delve on classes, but suffices to say that these functions are called methods. The String data type is actually a class called String. Variables created with a class are usually called objects. In the case of the String class, because it is used as a data type, I will still call them variables. The String class has several methods associated with it. in the following function, we use the indexOf() and length() methods. The indexOf() method returns the index of the first occurrence of the character passed as an argument in the String. The length() method returns the number of characters in the String. To use a method associated to a variable, use the variable name followed by a period, followed by the method name, parentheses, and any parameter required by the method. It is like calling a function, but preceded by the variable name. Thus, to get the index of the position of letter ‘f’ within the characters String and initialize the index variable with it, use the following code:

    int index = characters.indexOf('f');

index will be initialized with the value 5, the index of the letter ‘f’ in the String characters.

The outputCharacter function introduces us to two fundamental programming structures. The for-loop, and the if-then-else structures.

if-then-else Control Structure

The if-then-else control structure allows a program to conditionally execute a portion of a program. It has the following structure:

    if (condition) {
        // Code to execute if condition is true
    }
    else if (other condition) {
        // Code to execute if other condition is true
    }
    else {
        // Code to execute if all preceding conditions are false
    }

Only the first if block is mandatory; the else if and else blocks are optional. The execution flow is as follows: if the condition specified within parenthesis after the first if is true, then the code within the curly braces following the if statement is executed; if the condition within the first if statement is false then the condition in the else if statement is checked; if the condition in the else if statement is true, then the code within the curly braces following the else if statement is executed; if the condition in the else if statement is false, then the code contained within the curly braces after the else statement is executed. There can be several else if statements, each testing a different condition.

for-loop Control Structure

The for-loop control structure allows the program to iterate through code several times until a condition is met. It has the following structure:

    for (initialization; condition; post-processing {
        // Code to execute until condition is met
    }

where initialization is a statement executed just before entering the loop; condition is a test performed at the beginning of the loop, if true, code within the for-loop curly braces is executed, if false, execution continues after the loop; and post-processing, is a statement executed after each iteration through the code within the curly brackets. For instance, in the following for-loop control structure:

    for (int i = 0; i < 10; i++) {
        // Code executed 10 times
    }

Variable i is initialized to 0 before entering the loop; i is tested if smaller than 10, since it is, the code within the curly braces is executed and i is available for use within this code; after the code within the curly braces is executed, the variable i is incremented by 1. A second iteration of the loop then starts and i is tested if smaller than 10. The loop continues until the condition i < 10 is false. The loop executes 10 times with values of i from 0 to 9.

outputCharacter Function

Looking at the function header, we find that it is similar to the previous functions, but has a parameter within the parentheses: char c. This defines a single character parameter to the function. Parameters allows the calling program to pass values to the function. In this case, the character to be output as Morse code. Within the outputCharacter function, variable index is set to the index of character c in the characters String. If the character is found, that is if index is greater or equal to 0, then we output the Morse code. To do so, we get the Morse code associated with the character using index to retrieve the Morse code string from codedCharacters. We then iterate through each character of the Morse code and output a dash, if the character is a hyphen or a dot otherwise using the outputDash() and outputDot() functions respectively. At the end of the character output, we wait an extra 2 units of time, totaling the 3 units of time required at the end of a Morse code character. The wait totals 3 units of time since we already introduced a single unit of time delay at the end of the dash or dot.

/* Function to output a single character */
void outputCharacter(char c) {

    // Find index of character to encode
    int index = characters.indexOf(c);
 
    // Ignore unencodable characters
    if (index >= 0) {

        // Encode Morse code and output it
        String code = codedCharacters[index];
        for (int i = 0; i < code.length(); i++) {
            if (code[i] == '-') {
                outputDash();
            }
            else { // if not '-', must be '.'
                outputDot();
            }
        }

        // wait 3 units at the end of the letter
        // (2 units assuming previous dot or dash)
        delay(UNIT_TIME*2);
    }
}

Encoding Text into Morse Code

The sentence function’s purpose is to output text as Morse code. It accepts the single String parameter: text. First, the function gets the length of the text and stores it in the len variable. It then iterates through the whole text String using i as the index in a for-loop structure from 0 until len – 1. It gets the character indexed by i and turns it to lowercase using the tolower() built-in function. If the character is not a space, it is output using outputCharacter(). If the character is a space, we wait an extra 4 units of time, totaling the 7 units of time required at the end of a word. The wait amounts to 7 units of time since we already added 3 units of time at the end of the previous Morse code character output.

// Function to encode a whole string
void sentence (String text) {
    // Compute length of character string
    int len = text.length();

    // Output each character in turn
    for (int i = 0; i < len; i++) {

        // Only lower case characters are encoded
        char c = tolower(text[i]);
        if (c != ' ') {
            outputCharacter(c);
        }
 
        // Spaces are encoded as 7 units,
        // (4 units assuming a previous character)
        else {
            delay(UNIT_TIME*4);
        }
    }
}

The Main Loop

Finally, this is the main loop(). This code is repeated over and over. It outputs the text “Mikes Electro Shack” in Morse code, then waits 28 units of time corresponding to 4 spaces between words. The delay is actually specified as 25 units of time since the last character output already added a 3 units of time delay. You can of course replace the text by any text you see fit.

void loop() {
    sentence("Mikes Electro Shack");
    delay(UNIT_TIME*25); // Wait 4 spaces at the end
}

What Next?

Through the implementation of this fairly simple program, we have seen two extremely important control structures, the if-then-else control structure and the for-loop control structure. We have seen that the String data type is implemented as a class and that there are methods associated with it. We also have seen a few String methods such as indexOf() and length().

This program can be put to use to learn Morse code, which is required to get one’s ham radio (amateur radio) license. Complete the list of characters to include numbers and punctuation marks and their equivalent Morse code to complete the experience.

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.

There is Beauty in Geeky Things!

Welcome to my blog about sharing my life long passion with electronics, software design and my newfound passion with micro-controllers and their open hardware and software platforms. This blog is a series of tutorials and experiments to allow hobbyists understand the basics of electronic design and software programming and be able to reproduce the experiments, add to them and create their own circuits and programs.

Experiments in this blog make use of the Arduino Uno, a micro-controller, and a few electronic components that come with starter kits that can be found on the Internet. Experiments were tested using SparkFun Inventor’s Kit. The kit comes with a SparkFun RedBoard, a version of the Arduino Uno micro-controller, a solderless breadboard, jumper wires and electronic components such as LEDs (Light Emitting Diodes), resistors, light and temperature sensors, and more.

I recommend to the beginning enthusiast to procure a kit, whether from Sparkfun or other suppliers as they contain instructions on how to setup the Arduino, on how to get necessary software from the Internet, and how to use the software to program the micro-controller and make it work. Here is a non-exhaustive list of starter kits found on the Internet in no particular order.

Once equipped with a kit, we will embark on a series of circuits and programs that will complement and further your starter kit experience with explanations, tips and ideas.

Progress lies not in enhancing what is, but in advancing toward what will be. — Khalil Gibran

Place_Jacques-Cartier_Jan_2006