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.
Breadboarding
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 # MIT License # 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 // MIT License // 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 bool value = digitalRead(INPORT); // 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.
Thanks for sharing this Mike! Your circuit worked great for connecting my Pi to a device which expected 5v inverted logic with just a few simple parts I had on hand.
LikeLike