## A Light Activated Switch

Up until now we mostly talked about digital signals and digital values. Digital signals are series of discrete values that vary at regular intervals of time in discrete steps. All digital signals can be represented as sequences of integers. There are a finite number of values each digital quantity can have, including when it is represented by a floating-point number. By contrast, analog signals are values that continuously vary with time. Analog signals can be represented using real numbers. Real-world measurements of voltage, current, or speed are analog signals. Since computers and processors can only work with digital values and signals, any real-world analog quantity or signal must be converted to its digital counterpart in order to be processed. Fortunately, Arduino boards have analog input ports that convert voltages into digital values.

The featured image at the beginning of this blog post is titled “Sodium Light Lit Tree at Rogerthorpe Manor” by Andy Beecroft, CC BY-SA 2.0 and was downloaded as-is from the Wikimedia Commons.

## Analog to Digital Conversion

The micro-controller on the Arduino board has an analog to digital converter (ADC) that converts the voltage found at any one of the analog input pins to an integer value between 0 and 1023. The analog to digital converter splits the input voltage range into 1024 equal steps. By default, the Arduino’s analog input voltage range is between 0 volt and the Arduino’s supply voltage (VCC), approximately 5 volts for the Arduino Uno. Each step in the range spans VCC/1024 volts or approximately 0.00488 volts (4.88 mV) for the Arduino Uno. So, if the voltage at the analog input pin is between 0 V and 4.88 mV then the digital value is 0; if it is between 4.88 mV and 9.76 mV then the digital value is one; and so on until the input value is between VCC − 4.88 mV and VCC producing a discrete value of 1023. Hence, the discrete value returned by the ADC is

n = V∙1024 / VCC

where n is the integer discrete value returned by the ADC and V is the voltage at the analog input pin of the Arduino.

Once the input voltage has been converted to a discrete digital step value, we may want to convert that digital number to its corresponding digital floating-point voltage value to be able to use it in equations or formulas. We could convert the digital value to two floating-point numbers corresponding to the voltage range covered by the digital value returned by the ADC. A value of n at the input pin corresponds to analog voltages between n∙VCC/1024 and (n + 1)∙VCC/1024. For instance, if the value sampled is 100, the corresponding voltages would be between 100∙VCC/1024 and 101∙VCC/1024, or approximately between 0.488 V and 0.493 V on the Arduino Uno. However, using two values is not very practical. Instead, we can use the average value between the two extremes and state that there is a possible error of up to half the value of a step. Using our example, the average voltage for a sample value of 100 would be

(100∙VCC/1024 + 101∙VCC/1024) / 2 with a maximum error of ±0.5∙VCC/1024

On the Arduino Uno, by default, the voltage corresponding to a value of 100 is thus approximately 0.491 V ± 2.44 mV. More generally, the equation to convert the value returned by the ADC to a floating-point voltage value is

Voltage = (n + 0.5)∙VCC/1024 ± 0.5∙VCC/1024

Where n is the digital value returned by the ADC. The reason we used VCC in the equation is that the actual power supply voltage for the Arduino Uno is never quite 5 volts and the value measured and returned by the ADC is relative to VCC. In applications requiring more accuracy, the actual supply voltage would have to be measured, or better yet, a precise and stable voltage reference would have to be supplied. The conversion of input voltages into digital values by the ADC and the conversion from the digital value to its floating-point value can be visualized in the following graph.

In the graph, VCC has been changed to Vref, the reference voltage of the ADC which is VCC by default on the Arduino Uno.

## An Automatic Light Switch

Light intensity is an analog real-world value that can be measured and converted to a voltage. Several light sensitive devices can be used to do so, and one such device is the photo-resistor. In the next few sections, we will see how a photo-resistor can be used to convert light intensity into a measurable voltage, how to read from an analog input pin on the Arduino Uno, and how to turn a light appliance on when surrounding light intensity drops below a predetermined threshold. But first, what is a photo-resistor?

### The Photo-Resistor

A photo-resistor, or LDR for Light Dependent Resistor, is a two-terminal passive device whose resistance varies according to the amount of visible light reaching it. The resistance across its leads decreases as more light reaches its detection surface. As for any electronic device, the photo-resistor has specifications detailing its characteristics. Some of the main characteristics are

• Light resistance at 10 lux. This is the resistance in ohms across the device’s leads when 10 lux of light illuminates the device. The lux (symbol: lx) is a measure of illuminance, the amount of light that hits or goes through a surface. 10 lx approximately corresponds to the amount of light produced by a 60-watt incandescent light bulb reaching a one square meter surface 2.5 meters away from the light bulb.
• Gamma (γ). This is the relationship between how the resistance changes with respect to light intensity changes. The value corresponds to the difference of the logarithm of the resistance divided by the difference of the logarithm of illuminance. γ = (log R1 − log R2) / (log I2 − log I1), where R1 is the resistance when illuminance is I1 and R2 is the resistance when illuminance is I2. Note that the order of resistance values is reversed from the order of illuminance values in the equation.
• Dark resistance at 0 lux. This is the resistance in ohms across the device leads when no light reaches the device.
• Power dissipation (at 25 °C). The maximum power the device can dissipate at a temperature of 25 °C.
• Max voltage (at 25 °C). The maximum voltage the device can withstand between its leads at a temperature of 25 °C.

The symbol for a photo-resistor is shown above on the right. Also pictured on the left, photo-resistor GL5528, which has the following electrical characteristics:

• Light resistance at 10 lux: 8 KΩ to 20 KΩ
• Gamma (γ): 0.7
• Dark resistance at 0 lux: 1.0 MΩ (minimum)
• Power dissipation (at 25 °C): 100 mW
• Max voltage (at 25 °C): 150

### Computing Resistance and Illuminance

There are two characteristics of the photo-resistor that can help us determine its resistance for a given illuminance: the light resistance at 10 lux, R10lx, and the gamma, γ. Let’s take the gamma equation stated earlier in the photo-resistor characteristics section:

γ = (log R1 − log R2) / (log I2 − log I1)

Substituting R1 with the light resistance at 10 lux, R10lx, and I1 with 10 lx we get the following equation:

γ = (log R10lx − log R) / (log I − log 10)

where R is the unknown resistance value for a given illuminance I. Using the logarithm laws, we can solve the equation for R, which gives:

R = (10γ∙R10lxγ) / Iγ

Similarly, we can solve the same equation for I, yielding:

I = (10∙R10lx1/γ) / R1/γ

The light resistance at 10 lux for photo-resistor GL5528 is between 8 KΩ and 20 KΩ according to its electrical specification. We will use an average light resistance at 10 lux of 14 KΩ. The gamma for photo-resistor GL5528 is 0.7 for illuminances between 10 lx and 100 lx. Substituting the light resistance value at 10 lux, the gamma and an illuminance of 10 lx in the above equations we get:

R = (100.7∙14,0000.7) / I0.7  or
R ≈ 70,000 / I0.7

and

I = (10∙140001/0.7) / R1/0.7  or
I ≈ 8,400,000 / R1.43

### Converting Light Intensity to Voltage

We will use a voltage divider, as described in post “A Better Transistor Switch Circuit,” as a mean to convert photo-resistor electrical resistance into voltage. The following circuit diagram depicts the voltage divider circuit and its connection to the Arduino micro-controller.

We have to choose a resistance, R1, which limits enough current so that the phot-resistor does not overheat and so that the voltage at the analog input is close to the midpoint of the full voltage range. According to the GL5528 photo-resistor characteristics, the maximum power that the photo-resistor can dissipate is 100 mW at 25 °C. As we have previously seen in the Arduino’s Blink post, power dissipated through both the resistance and the photo-resistor is obtained by multiplying voltage and current together,

P = V∙I

and since I = V/R according to Ohm’s law, replacing I in the previous equation and R with the sum of both resistors, we get

P = V2 / (R1 + R2)

We know that the voltage, VCC, is approximately 5 volts and that P must be smaller than 0.1W. Hence, the following must hold:

V2 / (R1 + R2) < 0.1 W,
25 V2 / 0.1 W < R1 + R2

The sum of the resistance of both resistors must be larger than 250 Ω. Also, since we want to activate the light when the light intensity is below 10 Lux, or when the photo-resistor’s resistance is at or below 14 KΩ. Choosing resistor value of 15 KΩ for R1 satisfies both conditions. If R1 is 15 KΩ, the sum of both resistances is certainly larger than 250 Ω. Moreover, the voltage at the analog input pin will be approximately 2.41 V since

I = V / (R1 + R2) = 5 V / (15 KΩ + 14 KΩ) ≈ 0.172 mA

and

VR2 = R2∙I = 14K ∙ 0.172 mA ≈ 2.41V

The complete circuit becomes

We have the light detection part of the circuit. To activate the light, we will build on the circuit developed as part of the “A Better Transistor Switch Circuit” post and replace the push button switch with a GL5528 photo-resistor and connect the photo-resistor to an analog input pin instead of a digital input pin. The complete circuit is as follows:

The following picture depicts how to connect the different parts using a solderless breadboard, jumper wires, a transistor, a diode, a relay, a photo-resistor, a 15K resistor, a 1.8K resistor and a 3.3K resistor. Connections to the household appliance are not shown.

## The Light Activated Switch Program

We want the circuit to switch the lights on at dawn when it becomes dark and to switch the lights off in the morning when it is bright again. So, we want to switch the lights on when the illuminance at the photo-resistor falls below a preset value. Switching the lights on increases light intensity and turning them off when illuminance is above the same preset value as when we turned them on will cause oscillations, turning the lights repeatedly on and off. To prevent these unwanted oscillations, we need to implement hysteresis. Hysteresis is the dependence a system’s state has on its previous states; in our case, lights are to switch on when illuminance falls below a preset value only if their current state is off; and to switch them off if the illuminance is much higher than what it was when the lights were turned on only if their current state is on. Hysteresis will prevent oscillations if the illuminance at which the lights are to turn off is higher than the sum of the illuminance at which the lights are to turn on and the additional illuminance introduced by switching the lights on.

For example, let’s say a light appliance is to turn on when the light intensity caused by environmental lighting makes the illuminance at the photo-resistor fall below 10 lx. The light appliance being turned on causes an additional illuminance of 60 lx that can be measured at the photo-resistor. At this point, the illuminance at the photo-resistor is 70 lx. Turning the light appliance off only when the illuminance at the photo-resistor reaches 10 lx more, for a total of 80 lx, allows the lights to be turned off when the illuminance at the photo-resistor caused by environmental lighting is 20 lx, thus preventing any unwanted oscillations and turning the light appliance off at an appropriate light intensity.

In other words, the program must

• Turn the light appliance on, if it was previously off, when the illumination falls below 10 lx.
• Turn the light appliance off, if it was previously on, when the illumination rises above 80 lx.

We have already determined, from the photo-resistor specification that the device’s resistance at 10 lx is 14 KΩ on average. Using the equations developed earlier, the photo-resistance at 80 lx is 70,000 / 800.7 Ω, or approximately 3.3 KΩ. The voltage, V10, at the photo-resistor at 10 lx is

V10 = (5 V ∙ 14 KΩ) / (15 KΩ + 14 KΩ) ≈ 2.41V.

The voltage, V80, at the photo-resistor at 80 lx is

V80 = (5 V ∙ 3.3 KΩ) / (15 KΩ + 3.3 KΩ) ≈ 0.902 V.

As seen earlier, the integer value, n, read from the ADC is n = V∙1024 / VCC, where V is the voltage at the analog input. Thus, the integer value read from the ADC when the illuminance at the photo-resistor is 10 lx, and the voltage is 2.41 V, is 493. Similarly, the integer value read from the ADC when the illuminance at the photo-resistor is 80 lx, and the voltage is 0.902 V, is 184. Because the voltage varies inversely to luminance, the following will be implemented:

• Turn the light appliance on, if it was previously off and the analog input reads greater than 493.
• Turn the light appliance off, if it was previously on and the analog input reads less than 184.

The following Arduino program completes the post. Cut and paste the code in your Arduino IDE and download it to complete the project. It will turn the light appliance on when it gets dark and off again in the morning.

``````/*
Light Dependent Resistor (LDR) Sensor Analog Reader Sketch
Program that reads light intensity as an integer value from
a GL5528 sensor, turns on a light appliance when the
illuminance is smaller than 10 lx, if the light appliance
is already off, and turns the light appliance off when the
illuminance is greater than 80 lx, if the light appliance
is already on. The program is associated with the "A Light-
Activated Switch" blog post on https://lagacemichel.com
*/

// digital output port
#define LDR_PORT A0
#define LIGHT_SWITCH 11

// LDR voltage at which to turn the lights on and off
const int V10 = 493; // Analog value when LDR is 10 lx
const int V80 = 184; // Analog value when LDR is 80 lx
const int WaitInterval = 1000; // Interval between each loop

// Light appliance state
static bool LightOn = false;

// Setup the board.
void setup() {
pinMode(LIGHT_SWITCH,OUTPUT);
LightOn = false;
digitalWrite(LIGHT_SWITCH,LightOn);
}

// This is the main loop. It acquires the luminance then turns
// on the light appliance if it is off and the illuminance
// falls below 10 lx (the voltage is above 2.41V), and turns
// off the light appliance if it is on and if the illuminance
// increases above 80 lx (the voltage is below 0.902 V).
void loop() {

// Get sensor analog value from GL5528 sensor

// If light is below 10 lux and currently off, turn on the light
if (!LightOn && (LdrIntegerValue > V10)) {
LightOn = true;
}
// If light is above 80 lx and currently on, turn off the light
else if (LightOn && (LdrIntegerValue < V80)) {
LightOn = false;
}
digitalWrite(LIGHT_SWITCH,LightOn);

// Wait a while before repeating
delay(WaitInterval);
}``````

Following the usual header, you will find definitions for LDR_PORT, the analog input port to read illuminance from, and LIGHT_SWITCH, the digital output port to control the light appliance. We then define three constants. First, V10, the voltage above which (or illuminance below which) to turn the light on, V80, the voltage below which (or illuminance above which) to turn the light off, and WaitInterval, a constant in milliseconds determining the time to wait between loops. Finally, the Boolean variable LightOn holds the on or off state of the light appliance, HIGH for turned on and LOW for turned off.

In the setup() function, we prepare the light appliance control by setting the LIGHT_SWITCH digital port to OUTPUT, and turn the light appliance off. In the loop() function, we read the analog input into variable LdrIntegerValue. If illuminance is below 10 lux, the voltage above V10, and currently off, turn the light appliance on. If illuminance is above 80 lx, the voltage below V80, and currently on, turn the light appliance off. Wait the prescribed amount of time and repeat.

## WARNING:

The project in this post involves household mains high-voltages. Use caution whenever dealing with high-voltage wiring, including following directions carefully and following general safety practices. Safe assembly and operation of this project is the user’s responsibility. If unsure or if local laws prohibit the assembly of high-voltage circuits, get the help of a professional electrician. Do not make changes to the system while the device is plugged in.

## Sensing Temperature and Humidity

The DHT22 sensor, pictured below, is a small 25mm by 15mm by 8mm white plastic device with slits and holes organized in a grate pattern on its front face allowing for the surrounding air to reach within the sensor. It has four metal leads allowing it to be electrically connected to a circuit. The leads, numbered 1 to 4, from left to right, have the following functionality. Lead 1 is VDD, the power supply, whose voltage must be between 3.3 volts and 5.5 volts. Lead 2 is the data signal lead. We will describe how data is requested and transmitted later on in this post. Lead 3 is unused and should remain unconnected. Finally, lead 4 is ground and must be connected to the power source ground.

## Communicating with the DHT22

Communication with the sensor is achieved using the DHT22 data signal lead. The data signal lead is a bidirectional communication signal line. This means that the DHT22 sensor can receive and send information on this line. In it’s idle state, the sensor listens to the signal line waiting for a low, 0 volt, to be applied on the line for at least one millisecond by the Micro Controller Unit (MCU), the Arduino. 20 to 40 microseconds after the low signal reverts to high, the sensor sends a low, 0 volt, for 80 microseconds, then a high, 5 volts, for 80 microseconds to indicate start of transmission. The sensor then sends a series of low and high values, varying in duration, encoding zeroes and ones that will translate into numbers representing the temperature and the relative humidity. The following timing diagram illustrates this sequence.

The sequence of low and high values is similar to the dots and dashes that were discussed in the Morse Code Generator post. For Morse code, the duration of the dot, the short burst, is the unit of time by which all other elements of Morse code are defined. As can be seen in the DHT22 signal timing diagram below, a zero is encoded as a low value lasting 50 microseconds followed by a high value lasting 26 to 28 microseconds and a one is encoded as a low value lasting 50 microseconds followed by a high value lasting 70 microseconds. Thus, the unit of time is determined by how long the low signal lasts. A zero is detected when the low signal is followed by shorter high signal and a one is detected when the low signal is followed by a longer high signal. There are a total of forty of these low and high values forming a sequence of forty zeroes and ones, a sequence of forty bits.

### Relative Humidity

The first sixteen bits, or binary digits, represent the relative humidity value in thousandths. The way binary digits work is the same as for decimal digits except that there are only two possible values for each digit, 0 and 1. The minimum relative humidity value is 0, 0.0%, and the maximum relative humidity value is 1000, or 100.0%. The decimal value for the maximum relative humidity value is interpreted thus:

1×103+0×102+0×101+0×100,
1×1,000 + 0×100 + 0×10 + 0×1.

Each decimal digit represents the value of the digit multiplied by increasing powers of 10, from right to left. The same value represented using binary digits is 1111101000 or

1×29+1×28+1×27+1×26+1×25+0×24+1×23+0×22+0×21+0×20,
1×512 + 1×256 + 1×128 + 1×64 + 1×32 + 0×16 + 1×8 + 0×4 + 0×2 + 0×1.

Each binary digit represents the value of the digit multiplied by increasing powers of 2, from right to left. Most computers, microprocessors and micro-controllers, the Arduino is no exception, store information in chunks of eight bits, called bytes. As demonstrated above, to represent decimal 1000 in binary requires 10 bits of information. Since this information is stored in 8-bit bytes, 2 bytes of information, or 16 bits, are required to store a value ranging from 0 to 1000. So 1000 stored in binary in chunks of 8-bit bytes is

00000011 11101000

Where the byte on the left is the most significant byte containing the most significant bit also at the left. This is the first bit read from the sensor, and the byte on the right is the least significant byte containing the least significant bit, the last one read, completely at the right. The 16 bits returned by the DHT22 sensor device for a relative humidity of 100% are exactly as depicted, starting at the most significant bit, six zeroes, followed by 5 ones, then one zero, one one, and finally three zeroes.

### Temperature

The next 16 bits of the 40 bit sequence contain the temperature. Temperature is in degrees Celsius and span from −40.0 degrees to 80.0 degrees. Temperatures from 0 to 80 degrees are stored the same way as relative humidity values are encoded. 80 degrees is encoded as 800 decimal or 1100100000 in binary which can be stored in 2 bytes as

00000011 00100000

What about negative numbers, values between -40.0 and 0.0 degrees Celsius? In early computers, negative numbers were identified by setting the most significant bit to 1. Hence, five was encoded in binary as 00000101 and minus five as 10000101. This worked, but it was not very convenient as each operation had to check for the value of the most significant bit to decide which operation to perform and because there were two values for zero, a minus zero, 10000000, and a plus zero, 00000000. Another method was devised, the two’s complement method for representing negative numbers. It consists in complementing the binary number, making all zeroes ones and all ones zeroes, and then adding one. For instance, the number five, encoded as 00000101, becomes 11111010 and, if we add one, 11111011. Using the two’s complement negative number representation, negative numbers can be also be identified with their most significant bit, the leftmost bit, set to one.

The interesting thing is this: if you add a number with its two’s complement, the result is zero, exactly what one would expect when adding a number and its negative. The two’s complement of a number that has been two’s complemented is the original number, again, what is expected. Finally, the two’s complement of zero is also zero. These properties of the two’s complement method for representing negative numbers correspond to the mathematical properties of negative numbers, making arithmetic operations straight forward. If Tc(n) is a function returning the two’s complement of a number, we get the following properties for binary operations along with the corresponding properties for arithmetic operations.

So, the DHT22 sensor representation of minus forty is as follows. First, let’s encode forty degrees as 400, ten times forty. The 16-bit binary representation of 400 is 00000001 10010000. To make it negative, we complement the number, making all zeroes ones and all ones zeroes getting 11111110 01101111, and then we add one, getting 11111110 01110000. Temperatures vary in the range from -40.0 degrees Celsius to 80 degrees Celsius which becomes in binary terms:

from 11111110 01110000 to 00000011 00100000.

### Checksum

The final 8 bits sent by the DHT22 sensor contain a checksum. Checksums are a method to verify that the data sent is valid by making an arithmetic sum of all values sent and sending a truncated version of the sum last, allowing the receiving device to verify that data was transmitted correctly. The checksum sent by the DHT22 sensor is the sum of the first four 8-bit bytes truncated to the least significant 8 bits. After receiving all 40 bits of data from the sensor, the Arduino program computes the sum of the first four 8-bit bytes, truncates the result to the least significant 8 bits and compares the value to the checksum sent by the sensor. If the values match, the data is deemed valid.

## Connecting the DHT22 to an Arduino

The following picture depicts how to connect the different parts using a solderless breadboard, jump wires, and a DHT22 temperature and relative humidity sensor. The sensor’s lead 1, the leftmost one, is connected the 5 volts supply, lead 4, the rightmost one, is connected to the supply’s ground, and lead 2, the signal lead, is connected to digital I/O pin 2.

## The Program

The purpose of the program is simple, read temperature and relative humidity from the DHT22 sensor and return the information to the connected computer. The following shows the content of the main sketch which can be found, along with the DHT22 access class, on GitHub.

``````/*
DHT22 Temperature / Humidity Serial Decoder Sketch
Program that requests temperature and humidity from a DHT22 sensor
and returns the results to the serial port. It is associated with
the Serial Communication with a Temperature Sensor blog post on
https://lagacemichel.com
*/

#include "DHT22.h"

// DHT serial input/output port
#define DHT22_PORT 2

// Create DHT22 device instance
DHT22 dht(DHT22_PORT);

// Setup the board.
void setup() {
Serial.begin(9600);
}

// This is the main loop. It requests for a transmission, decodes
// the signal and displays decoded temperature and humidity.
void loop() {
// Get relative humidity and temperature
float relativeHumidity = dht.relativeHumidity();
float temperature = dht.temperature();

// Send results back to PC
Serial.print("T: ");
Serial.print(temperature,1);
Serial.print("C RH: ");
Serial.print(relativeHumidity,1);
Serial.println("%");
delay(5000);
}``````

First, the Arduino sketch file starts with a comment stating the purpose of the program, the author and a copyright notice. The program is licensed under the standard MIT license. Next, we include the “DHT22.h” file which declares the DHT22 class. We will cover the content of this file later in this post. Classes and objects were discussed previously in the Programming with Class blog post. We then define the port to use to communicate with the sensor, DHT22_PORT and we instantiate an object of class DHT22, initializing it with the port just specified. The DHT22 object will handle all communications with the external sensor to provide the program with the temperature and relative humidity.

The setup() function sets up the serial communication to communicate with the PC at 9600 baud, or approximately 10 characters per second. In the main loop() function, we get the relative humidity and temperature from the instance of the DHT22 class using the relativeHumidity() and temperature() methods respectively. We then send the information back to the connected computer using the Serial interface’s print() and println() methods. Finally, the program waits five seconds, using the delay() function, before exiting the loop() function which will be repeatedly called forever.

## The DHT22.h File

The DHT22.h header file defines the DHT22 class. C++ and .ino files that want to use objects of the DHT22 class must include this file using the ‘#include‘ statement. The header file contains the class definition that declares a destructor and a constructor using the port number to which the DHT22 sensor is connected in its public section. Two accessor methods, temperature()and relativeHumidity() are declared to return the floating point value of the temperature and relative humidity read from the sensor.

Follows a private section declaring the default constructor, the copy constructor and the assignment operator. They are made private to prevent their use. Then we declare three private methods, timeSinceLastRead(), waitForState(), and fetchData(), only accessible from within the class. These methods will be described later in the post. Finally, there is a private section containing the persistent data used within the class. This data includes an integer (int) holding the port number, m_port, used to communicate with the sensor, an array of five bytes (byte) m_data, to contain the forty bits read from the sensor, an unsigned long integer, m_lastRead, containing the last time, in milliseconds since the program started, data was read from the sensor, two floating point numbers (float), m_temperature and m_relativeHumidity, containing the last temperature and relative humidity read from the sensor, and a Boolean flag (bool), m_firstTime, used to indicate if the class instance has been previously used.

``````/*
DHT22 Temperature / Humidity Controller Class Header
DHT22 temperature and relative humidity sensor handling class. This class
allows the instanciation of objects that communicate with a DHT22 sensor
connected to a digital input/output port on an Arduino micro-controller.
This code is associated with the Serial Communication with a Temperature
Sensor blog post on https://lagacemichel.com
*/

#if !defined(DHT22_H)
#define DHT22_H

#include "Arduino.h"

class DHT22 {
public:
// Orthodox cannonical form
~DHT22(); // Destructor

// Constructor connecting digital I/O port to DHT22 device
DHT22(int port); // Constructor

// Get temperature and relative humidity
float temperature();
float relativeHumidity();

private:
// Unusable and hidden orthodox cannonical form
DHT22();  // Default constructor
DHT22(const DHT22&); // Copy constructor
DHT22& operator = (const DHT22&); // Assignment operator

// Internal methods
int waitForState(bool state) const;
void fetchData();

private:
int m_port;               // I/O pin connected to sensor
byte m_data;           // Data read from DHT22 sensor
unsigned long m_lastRead; // Last time data was read in milliseconds since program start
float m_temperature;      // Temperature in degrees Celsius
float m_relativeHumidity; // Relative humidity in %
bool m_firstTime;         // Flag if first time around
};
#endif``````

## The DHT22.cpp File

The “DHT22.cpp” file contains the implementation of the DHT22 class methods. It starts with the standard header with title, description, license and copyright notice in a comment. It then includes the class definition from header file “DHT22.h” and defines a few timing values that will be used throughout the code. The CYCLES_PER_COUNT definition is the number of CPU cycles that the waitForState() method uses on average every time it checks if the signal from the DHT22 sensor has achieved the desired level, HIGH or LOW. This value was measured as part of a separate benchmark program. The TIMEOUT_MICROSECONDS value is the maximum time, in microseconds, to wait for the desired level. The TIMEOUT value corresponds to the number of times the signal level needs to be checked to reach a timeout. The TIMEOUT value is defined using the F_CPU definition found in the “Arduino.h” header file. F_CPU defines the CPU frequency in cycles per second for the Micro Controller used. For the Arduino Uno, this value is 16,000,000. The last three definitions specify in milliseconds delay values of ONE_SECOND, TWO_SECONDS, and TWO_MILLISECONDS.

``````/*
DHT22 Temperature / Humidity Controller Class Body
DHT22 temperature and relative humidity sensor handling class. This class
allows the instanciation of objects that communicate with a DHT22 sensor
connected to a digital input/output port on an Arduino micro-controller.
This code is associated with the Serial Communication with a Temperature
Sensor blog post on https://lagacemichel.com
*/

#include "DHT22.h"

#define CYCLES_PER_COUNT 50
#define TIMEOUT_MICROSECONDS 300
#define TIMEOUT TIMEOUT_MICROSECONDS/CYCLES_PER_COUNT*F_CPU/1000000
#define ONE_SECOND 1000
#define TWO_SECONDS 2000
#define TWO_MILLISECONDS 2``````

### DHT22 Class Constructor and Destructor

The class constructor initializes the DHT22 sensor port value, m_port, to the value passed as a parameter to the constructor. All data values in the m_data array are initialized to 0, the last time in milliseconds since the program started that the sensor was read, m_lastRead, is also initialized to 0. The first time through flag, m_firstTime, is set to true, and both floating point values for the temperature and relative humidity, m_temperature and m_relativeHumidity, are initialized to 0.0. The DH22 class destructor does not perform any action.

``````// DH22 constructor, accepts Arduino digital I/O port number
DHT22::DHT22(int port) {
m_port = port;
m_data = 0;
m_data = 0;
m_data = 0;
m_data = 0;
m_data = 0;
m_firstTime = true;
m_temperature = 0.0;
m_relativeHumidity = 0.0;
}

// DH22 destructor
DHT22::~DHT22() {
}``````

### DHT22 Class Accessors

Accessors are the class methods used to access the DHT22 sensor temperature and relative humidity. Both methods call the fetchData() method to read information, if required, from the DHT22 sensor, then return the sought information. The temperature() method returns a floating point value representing the temperature in degrees Celsius and the relativeHumidity() method returns a floating point value representing the relative humidity as a percentage.

``````// Return temperature
float DHT22::temperature() {
// Get data from sensor and return temperature read
fetchData();
return m_temperature;
}

// Return relative humidity
float DHT22::relativeHumidity() {
// Get data from sensor and return relative humidity read
fetchData();
return m_relativeHumidity;
}``````

### DHT22 Class timeSinceLastRead( ) Method

The timeSinceLastRead() method returns an unsigned long integer containing the time in milliseconds since the fetchData() method actually read data from the DHT22 sensor device. The fetchData() method stores the number of milliseconds since the program was started in variable m_lastRead every time it reads data from the sensor and it calls the timeSinceLastRead() method before attempting to read data from the sensor, ensuring that at least two seconds have elapsed since the last sensor device request.

The timeSinceLastRead() method first gets the current number of milliseconds since program start using the millis() function. It then checks if the value has overflowed, that is if the unsigned long value went further than the maximum value an unsigned long can hold. This value is actually 4,294,967,295, corresponding to approximately 49.7 days. After an overflow, the number of milliseconds since the program started restarts at zero. If an overflow occurs, time is computed by adding the two’s complement of the last time read corresponding to the time left to reach the overflow added to the number of milliseconds after the overflow, providing the actual number of milliseconds since the time was stored in m_lastRead. Two’s complement is computed by complementing the value, turning all ones into zeroes and all zeroes into ones using the unary bitwise not operator, ‘~‘, and then adding one. Otherwise, the method simply computes the difference between the current time and the last time m_lastRead was updated.

``````// Return the number of milliseconds since last time read
// Get current processor time
unsigned long currentMilliseconds = millis();
unsigned long timeSince = 0;

// Check if time wrapped around, if so use two's complement
timeSince = ~m_lastRead + 1 + currentMilliseconds;
}

// Otherwise use difference
else {
}

// Return elapsed time
return timeSince;
}
``````

### DHT22 Class waitForState( ) Method

The waitForState() method repeatedly checks if the DHT22 sensor data lead has reached the desired state, HIGH or LOW, as specified in the method’s parameter, state, or if a timeout has been reached. The method returns the number of times the sensor line has been checked, or 0 if a timeout occurred.

``````// Wait for serial line to reach a state and return relative time
int DHT22::waitForState(bool state) const {
int count = 0;
count++;
if (count >= TIMEOUT) {
count = 0;
break;
}
}
return count;
}``````

### DFT22 Class fetchData( ) Method

The fetchData() method is the actual method that reads data from the DHT22 sensor, through the waitForState() method, and that computes the temperature and relative humidity. First, the method checks if this the first time the method is being called or if more than two seconds have elapsed since the last time sensor data has been read. If not, then the method does nothing and returns. If it is the first time the method is called, the DHT22 sensor port is put in input mode with a pull-up resistor for one second. Then, the port is put in OUTPUT mode, a LOW signal is sent for two milliseconds, then the port is put back in INPUT_PULLUP mode, providing a HIGH signal at the port, thus completing the request for data signal.

Next, we enter the time critical section of the code where we will be measuring the timing of the signal sent back by the DHT22 sensor. For the whole period of a maximum of approximately 3 milliseconds, we disable interrupts, allowing us to precisely compute time. Because the Arduino’s time computing functions require interrupts to be enabled, we use the number of CPU cycles spent detecting a change of state, between HIGH or LOW, within the waitForState() method. Interrupts are disabled using the noInterrupts() Arduino built-in function and re-enabled using the interrupts() Arduino built-in function.

While interrupts are disabled, the code first checks for the signal to go from HIGH to LOW, then from LOW to HIGH, and back to LOW again. This corresponds to the start of transmission signal from the DHT22 sensor. The low and high signals last 80 microseconds each but there is no need to check the timing except for timeouts. This is why we check for a positive integer everytime waitForState() is called in order to continue processing. After sending the start of of transmission, the DHT22 sensor proceeds with the forty bits of data. For each bit, the counter values for the signal to go from LOW to HIGH and then from HIGH to LOW are gathered from appropriate calls to the waitForState() method. If there are no timeouts, the bit value is stored in the appropriate byte as explained after the code listing below.

``````// Fetch data from sensor
void DHT22::fetchData() {
// We will access sensor on the very first time and if more than two
// seconds have passed since last access.
if (m_firstTime || (timeSinceLastRead() > TWO_SECONDS)) {

// First time around, wait at least one second for sensor to settle
if (m_firstTime) {
m_firstTime = false;
pinMode(m_port, INPUT_PULLUP);
delay(ONE_SECOND);
}

// Send request signal to reMad temperature and relative humidity from device.
pinMode(m_port, OUTPUT);
digitalWrite(m_port, LOW);
delay(TWO_MILLISECONDS);
pinMode(m_port, INPUT_PULLUP);

// Since timings are critical, prevent interrupts while DHT22 transmits
noInterrupts();

// Get Start of Transmission, falling, rising, then falling edges
// Interval: 80 micro-seconds, no need to check
int lowCounter = 0;
int highCounter = 0;
highCounter = waitForState(LOW);
if (highCounter > 0) {
lowCounter = waitForState(HIGH);
if (lowCounter > 0) {
highCounter = waitForState(LOW);
}
}

// Get 40 bits of data and store them in the data array
if (highCounter > 0) {
for (int bitN = 0; bitN < 40; bitN++) {     // 40 bits, 5 x 8-bit bytes
lowCounter = waitForState(HIGH);          // Byte 0: Relative Humidity MSB
if (lowCounter > 0) {                     // Byte 1: Relative Humidity LSB
highCounter = waitForState(LOW);        // Byte 2: Temperature MSB
if (highCounter > 0) {                  // Byte 3: Temperature LSB
int byteN = bitN/8;                   // Byte 4: Checksum
m_data[byteN] <<= 1;
if (highCounter > lowCounter) {       // Low 50us, High 25us -> 0
m_data[byteN] |= 1;                 // Low 50us, High 70us -> 1
}
}
}
}
}
// Reception complete, interrupts are re-enabled
interrupts();

// Save the time of last data transfer

// Compute temperature and relative humidity if data is valid
byte checksum = m_data + m_data + m_data + m_data;
if (m_data == checksum) {
m_relativeHumidity = int((m_data << 8) + m_data)/10.0;
m_temperature = int((m_data << 8) + m_data)/10.0;
}
else {
m_relativeHumidity = 0.0;
m_temperature = 0.0;
}
}
}``````

In the middle of the loop that gathers the 40 bits above, we get the number of counts for which the signal is LOW and the number of counts for which the signal is HIGH in the lowCounter and highCounter variables respectively. As explained previously, the corresponding bit is ‘0‘ if highCounter is smaller than lowCounter and ‘1‘ if highCounter is greater than lowCounter. Each sequence of 8 bits is stored in its own byte in the m_data array, first bit read in the byte’s most significant bit. Bits 0 to 7 are stored in m_data, bits 8 to 15 in m_data, and so on. We determine in which m_data array element to store a bit by dividing the bit number, bitN, by 8 since there are 8 bits in each byte. Once we know which m_data array element to store the bit in, we do two operations. First we push the bits already in the array element by one bit to the left using the shift left operator (‘<<=‘). This is equivalent to multiplying the data by 2, pushing all bits to the left and setting the least significant bit to 0. If highCounter is larger than lowCounter, we then set the least significant bit of the array element to 1 using the ‘or’ binary operator, ‘|=‘. The ‘or’ operator takes each bit in the array element and performs an ‘or’ operation with each bit of the operand, in this case 1, or 00000001 in binary. If both bit values are ‘0‘ then the result is ‘0‘. If any of the bit values are ‘1‘ then the resulting bit is ‘1‘. At the end of the loop, all 40 bits are stored in the m_data array.

As soon as interrupts are re-enabled, we store the current time in m_lastRead, which will prevent the sensor from being read for the next two seconds. Finally, we compute the checksum of the first four bytes received from the sensor and compare it to the 5th byte received, the sensor checksum. If checksums match, we compute the temperature and relative humidity by creating a 16 bit unsigned value, shifting the most significant byte 8 bits to the left and adding the least significant byte. We then convert the number to a signed integer, and divide the signed number by 10.0, automatically converting the result to a floating point value. The temperature and relative humidity values are saved in the m_temperature and m_relativeHumidity variables respectively for future use. If there was a checksum error, both temperature and relative humidity values are set to 0.0.

## Putting It to Work

Build the circuit shown above and connect the Arduino Uno to your computer using a USB cable. On the computer, within the Arduino IDE, compile and download the sketch then press the control, shift, and M keys simultaneously. This will make the Serial Monitor window appear. Every five seconds, the Arduino program will send the current temperature and humidity as a character string back to the computer. The text is displayed on the Serial Monitor window and the output should look like the following:

## 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.
*/

// 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.

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:

## 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”.

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 newValue = startValue;
while (newValue == startValue) {
}
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;
}
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.

```/*
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
*/

define INPORT 8
define TIME_UNIT 50
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;
int elapsedTimeUnits =

// 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.

## Raspberry Pi Speaks Arduino

For a project of mine, I needed to connect a Raspberry Pi’s output to an Arduino Uno’s input. The Raspberry Pi’s digital input/output uses 3.3-volt logic while the Arduino Uno digital input/output uses 5-volt logic. How does one convert from one logic level to the other? There are several ways to do this and most methods use a transistor switch controlled by 3.3-volt logic to drive 5-volt logic.

We must first define what constitutes a HIGH and a LOW on the Arduino Uno and the Raspberry Pi. For the Arduino Uno, a digital pin, whether input or output, is considered in a LOW state for voltages below 1.5 volts and in a HIGH state for voltages between 3 and 5 volts. The Raspberry Pi’s digital input and output pins are considered in a LOW state for voltages below 1 volt and in a HIGH state for voltages between 2 and 3.3 volts.

# From 3.3 Volts to 5 Volts

In order to convert the Raspberry Pi’s digital output voltage levels to the Arduino’s digital input levels, we can use a transistor switch, similar to the one used to control a relay in a previous post. I will describe two transistor circuits: the logic inverter and the level converter using the transistor’s emitter as input.

## Logic Inverter

In the circuit below, we want that if the input voltage is between 0 and 1 volt, the transistor be in cutoff mode, completely off, and the output voltage be between 3 to 5 volts. We also want that if the input voltage is between 2 and 3.3 volts, the transistor be in saturation mode, completely on, and the output voltage be between 0 to 1.5 volts. In other words, if the input is LOW, the output will be HIGH and if the input is HIGH, then the output will be LOW. This circuit is called a logic inverter. It converts logic levels but it inverts logical values. Because the input is considered LOW at or below 1 volt and HIGH at or above 2 volts, we want for the transistor to go from cutoff to saturation mode between input values of 1 volt and 2 volts. To ensure that LOW and HIGH values are met, let’s make the transistor be in cutoff mode for input values at of below 1.4 volts and let it be in saturation mode for values at or above 1.6 volts.

In cutoff mode, there is no transistor base current and the base-emitter voltage (Vbe) is at or below 0.7 volts. Hence, the current in Rbase and Rbias are the same and the voltage across Rbias is at or below 0.7 volts. When the input voltage is 1.4 volts, the following equation holds.

Vbe / RBias = (Vin – Vbe) / RBase

0.7 V / RBias = (1.4 V – 0.7 V) / RBase = 0.7 V / RBase

RBias = RBase

In saturation mode, the voltage across the collector and emitter is 0 volts and collector current is constant at Vcc / RLoad. We want the collector current to be large enough for the transistor to work properly, but small enough to consume the least power possible. Using a 10K resistor, the collector current is 5 V / 10K, or 0.5 mA which is within the BC337-40 bipolar junction transistor working values. According to its specification, the hfe value, the collector-base current amplification, is 400. The base current is thus

Ib = Ic / hfe

Ib = 0.5 mA / 400 = 1.25 μA

The current through RBase is equal to the currents through both RBias and the transistor’s base.  When the input voltage is 1.6 volts, the following equation holds.

(Vin – Vbe) / RBase = Vbe / RBias + Ib

(1.6 V – 0.7 V) / RBase = 0.7 V / RBias + 1.25 μA

Since, as we have seen above, RBias has the same value as RBase, the equation becomes

0.9 V / RBase = 0.7 V / RBase + 1.25 μA

RBase = RBias = 160K ≅ 220K

The circuit becomes

The following graph plots the input and output computed voltage values, in blue, and the values measured on an actual circuit implemented with the resistor values in the schematic above, in orange.

As can be seen, logical values are inverted and voltage values correspond to acceptable values for both the Raspberry Pi and Arduino’s HIGH and LOW voltage levels. Notice the shift of the target voltage values slightly to the left of 1.5 volts. This is caused by the fact that we are not using the exact resistor values as computed, because the saturated Vbe is not exactly 0.7 volts and the actual hfe is not exactly 400. Still, the results are more than acceptable, if we wanted an inverted signal.

## Using the Emitter as Input

Let’s use the same circuit, but connect the base resistor to the Raspberry Pi’s supply, VccP, and the emitter to the Raspberry Pi digital output as in the following circuit.

This is like rotating the previous circuit clockwise by 90 degrees. Now we want that if the input voltage is LOW, below 1 volt, that the transistor be in saturation and the output be at Vin, the Raspberry Pi digital output value.  We also want that if the input voltage is HIGH, at 2 volts or higher, that the transistor be in cutoff and the output be at the Arduino’s supply voltage. Because the permissible voltages for the LOW value on the Arduino, between 0 and 1.5 volts, are higher than those for the Raspberry Pi, between 0 and 1 volt, it is acceptable to use the Raspberry Pi digital output voltages as the Arduino’s input for LOW values.

In this circuit, as it was in the previous one, in cutoff mode there is no transistor base current and the base-emitter voltage is at or below 0.7 volts. Hence, the current in RBase and RBias are the same and the voltage across RBias is at or below 0.7 volts. Hence, when the input voltage is 1.6 volts, the following equation holds.

(VccP – Vbe – Vin) / RBase = Vbe / RBias

(3.3V – 0.7V – 1.6) / RBase = 0.7V / RBias

RBias = 0.7 • RBase

For saturation mode, the same reasoning as for the logic inverter holds about the base current to collector current relationship. Since we want saturation of the transistor to occur at 1.4 volts, the collector current is equal to the current going through RLoad.

Ic = (VccA – Vin) / RLoad

Ic = (5V – 1.4V) / 10K = 0.36 mA

The base current is thus

Ib = Ic / hfe

Ib = 0.36 mA / 400 = 0.9 μA

The current through the base is the current through RBase minus the current through RBias. When the input voltage is 1.4 volts, when we want transistor saturation to occur, the following equations hold.

Ib = (VccP – Vbe – Vin) / RBase – Vbe / RBias

0.9 μA = (3.3V – 0.7V – 1.4V) / RBase – 0.7V / RBias

Since, as we have seen above, RBias is 0.7 • RBase, we have

0.9 μA = (3.3V – 0.7V – 1.4V) / RBase – 0.7V / (0.7 • RBase)

0.9 μA = 1.2V / RBase – 1V / RBase

RBase = 0.2V / 0.9 μA = 222.222K ≅ 220K

We then compute RBias from the cutoff equations

RBias = 0.7 • RBase = 155.556K ≅ 150K

The circuit becomes

The following graph plots the input and output computed voltage values, in blue, and the values measured on an actual circuit implemented with the resistor values in the schematic above, in orange.

In the graph, logical values are converted to the same levels this time, and voltage values correspond to acceptable values for both the Raspberry Pi and Arduino’s HIGH and LOW voltage levels. Notice the shift of the target voltage values slightly to the right of 1.5 volts. This is caused by the fact that we are not using the exact resistor values as computed, because the saturated Vbe value is not exactly 0.7 volts and the actual transistor current amplification is not exactly 400. Nevertheless, results are more than acceptable.

# Putting it Together

Now that we have a method to convert 3.3-volt logic to 5-volt logic, we can connect a Raspberry Pi 3 GPIO output to an Arduino digital input. As a test, we will program the Raspberry Pi to output Morse Code, read the output using an Arduino digital input and output the value read to the Arduino’s built-in LED. The code within this post can be found on GitHub by clicking here.

First, let’s have a look at the complete circuit. The following picture depicts how to connect the different parts using a solderless breadboard, jump wires, a BC337-40 transistor, and a 10K, a 220K and a 150K resistor. 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.

## Morse Code Generator in Python

We have seen the morse code generator in a previous post. I have ported the Morse Code program to Python 3 for it to be run on the Raspberry Pi. I used IDLE (Integrated Development and Learning Environment) to code and run the Python 3 code on the Raspberry Pi. If you are new to the Raspberry Pi and want to set it up, please visit “Get started with Raspberry Pi” on the www.raspberrypi.org site.

```# Morse Code Generator Python Program that translates a
# text string to Morse Code and outputs it to a GPIO pin.
# This program is used as part of a demonstration to show
# connectivity between a Raspberry Pi and an Arduino UNO.
# It is associated with the Raspberry Pi Speaks Arduino
# blog post on https://lagacemichel.com
# Copyright (c) 2019, Michel Lagace

import RPi.GPIO as IO
import time
UNIT_TIME = 0.1
BOARD_PIN = 7
# Characters to be encoded
characters = "abcdefghijklmnopqrstuvwxyz"

# Morse code sequences for each character
codedCharacters = [
".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....",
"..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.",
"--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-",
"-.--", "--.." ]

# Setup the board. Digital port LED_BUILTIN in output mode
def setup():
IO.setwarnings(False)
IO.setmode (IO.BOARD)
IO.setup(BOARD_PIN,IO.OUT)

# Function to output a dot: one unit on, one unit off
def outputDot():
IO.output(BOARD_PIN,1)
time.sleep(UNIT_TIME)
IO.output(BOARD_PIN,0)
time.sleep(UNIT_TIME)

# Function to output a dash: three units on, one unit off
def outputDash():
IO.output(BOARD_PIN,1)
time.sleep(UNIT_TIME*3)
IO.output(BOARD_PIN,0)
time.sleep(UNIT_TIME)

# Function to output a single character
def outputCharacter(c):
# Find index of character to encode
index = characters.find(c)
# Ignore unencodable characters
if (index >= 0):
# Encode morse codeand output it
code = codedCharacters[index]
for ch in code:
if ch == '-':
outputDash()
else: # if not '-', must be '.'
outputDot()
# Wait 3 units at the end of the letter
# (2 units assuming previous dot or dash)
time.sleep(UNIT_TIME*2)

# Function to encode a whole string
def sentence (text):
# Output each character in turn
for ch in text:
# Only lower-case characters are encoded
if ch != ' ':
outputCharacter(ch.lower())
# Spaces are encoded as 7 units,
# (4 units assuming a previous character)
else:
time.sleep(UNIT_TIME*4)

# Function looped indefinitely
def loop():
sentence("Mikes Electro Shack")
time.sleep(UNIT_TIME*25) # Wait 4 spaces at the end

# Main program. Setup board then loop forever
setup()
while(True):
loop()```

## Raspberry Pi Reader on the Arduino

The code on the Arduino is quite simple. It loops forever, reading the digital input connected to the Raspberry Pi, writes the value to the built-in LED, then waits 10 milliseconds befor starting over.

```// Main RaspberryPiReader sketch
// Program that reads a digital input from a Raspberry Pi
// GPIO pin and outputs its value to an Arduino digital output
// pin. This sketch is used as part of a demonstration to show
// connectivity between a Raspberry Pi and an Arduino UNO.
// It is associated with the Raspberry Pi Speaks Arduino
// blog post on https://lagacemichel.com
// Copyright (c) 2019, Michel Lagace

// Define input and output ports
#define INPORT 8    // Input port connected to Raspberry Pi
#define OUTPORT 13  // Output port to built-in

// Time to wait in milliseconds between samples
#define SAMPLE_DELAY 10

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

// Repeat forever
void loop() {
// Read value from Raspberry Pi

// Output value to built-in LED
digitalWrite(OUTPORT, value);

// Wait before next sample
delay(SAMPLE_DELAY);
}```

Download the sketch on the Arduino then run the Python program on the Raspberry Pi. You will notice Morse Code being output by the Arduino’s buit-in LED.

## A Better Transistor Switch Circuit

In the previous post, we have seen how to drive a relay using an NPN bipolar junction transistor. The circuit allowed a digital input signal to activate and deactivate a relay coil. As seen previously, the following graph depicts what is happening to the relay coil voltage drop with respect to the input voltage from an Arduino digital output pin.

Looking at the bottom horizontal band, the relay electrical characteristics guarantee that the relay is off for input values between 0 volts and 1 volt; and looking at the top horizontal band, the relay electrical characteristics guarantee that the relay activates when the input voltage is above approximately 2.75 volts. What if we wanted the relay to remain off until the input voltage to be at least 2 volts, and the relay to be on when the input voltage is at most 3 volts?

# Biasing the Base of the Transistor

There is a way in our circuit for base-emitter voltage drop not to be equal to Vin when the transistor is in cut-off mode. Remember that the transistor requires a forward voltage drop of 0.7 volts, for silicon-based bipolar junction transistors, between the base and emitter before current can flow through the base and thus the collector. While current is not flowing through the base or base resistor, the voltage at the base is the same as the voltage at the input. How can we make the voltage at the input of the base resistor higher than the voltage at the base? By using a voltage divider.

## The Voltage Divider

The voltage divider is a simple circuit where two resistors in series divide the voltage amongst them. According to Kirchhoff’s voltage law, as seen in the Blink post, the sum of the electromotive forces, as provided by batteries, in any closed loop is equivalent to the sum of the voltage drops in that loop. Thus, in the following circuit,

The sum of voltage drops across R1 and R2 is equal to VDC. The current IDC in the circuit is, according to Ohm’s law, I = V/R or

IDC = VR1/R1 = VR2/R2
IDC = (VR1 + VR2) / (R1 + R2)
IDC = VDC / (R1 + R2)

The voltage across R2 is

VR2 = IDC•R2
VR2 = VDC•R2 / (R1 + R2)

If VDC is 2 volts and we want VR2 to be 0.7 volts then

0.7 V = 2 V•R2 / (R1 + R2)
0.35•R1 + 0.35•R2 = R2
0.35•R1 = 0.65•R2

Lets apply this to the complete switch circuit from the previous post and add a resistor, RBias between the base of the transistor and ground.

In this circuit, as previously computed, in order for the Vin value of 2 volts to produce a voltage of 0.7 volts across the base and emitter of the transistor, RBase and RBias must satisfy the following equation

0.35•RBase = 0.65•RBias
RBase = 0.65•RBias / 0.35

When the base-emitter voltage reaches 0.7 volts, current starts flowing into the base of the transistor and the base-emitter voltage remains 0.7 volts. According to Kirchhoff’s current law the sum of currents flowing into the junction of the base resistor, the bias resistor and the base node is equal to the sum of currents flowing out of that junction. The current flowing into the junction through the base resistor is

I = (Vin – 0.7 V) / RBase

The current flowing out of the junction through the bias resistor and the transistor’s base is

I = 0.7 V / RBias + IBase

Hence,

IBase = (Vin – 0.7V) / RBase – 0.7 V / RBias

Now, assuming a resistive relay coil load of 70 Ω, a power supply of 5 V and a transistor hFE of 250, the base current at start of transistor saturation is

IBase = 5 V / (70 Ω•250) ≅ 0.286 mA

As stated earlier, we want Vin to be 3 volts at saturation and using the base current equation, we get

0.286 mA = (3 V – 0.7V) / RBase – 0.7V / RBias

Replacing RBase with the voltage divider circuit equation computed for the resistor values at cutoff we get

0.286 mA = (3V – 0.7V) / (0.35 RBias/0.65) – 0.7V / RBias
RBias = 1795 Ω ≅ 1.8K

Replacing RBias in the cutoff voltage divider equation we get

RBase = 0.65•RBias / 0.35 = 3333 Ω ≅ 3.3K

## Resulting New Circuit

The following diagram depicts the transistor relay switch circuit with a voltage divider at the base of the transistor switch. The values of the resistors are 3.3K for the base resistor and 1.8K for the bias resistor.

The following graph depicts what is now happening to the relay coil voltage with respect to the input voltage from an Arduino digital output pin when the voltage divider is used at the base of the transistor.

Notice how we get a cleaner and sharper voltage transfer between the input signal and the voltage at the relay coil. This new circuit ensures that weaker input digital signals can be used to operate the transistor relay switch.