Logic levels are the voltage ranges that a circuit interprets as a 0 or a 1. There is no single exact voltage for each state, but rather input thresholds and safety margins that depend on each logic family.
Today we live in a hybrid world. We have old boards working at 5V and modern sensors or microcontrollers (ESP32, Raspberry Pi, STM32…) working at 3.3V. Connecting them without thinking is a great way to release magic smoke and learn the hard way.
Physical reality is messy. What happens if a wire has 4.5V? Is it a 1? What about 2.5V? Is it a 1, a 0, or an error?
Manufacturers define thresholds to ensure one chip understands another, and not all families use the same values. Let’s compare TTL and CMOS and see what happens when connecting 5V with 3.3V.
Logic Families
TTL (Transistor-Transistor Logic)
This is the classic technology (74LSxx series), based on BJT transistors.
- Standard supply voltage: 5V.
- Robust but consume considerable current (they heat up).
- Their thresholds are very permissive with the high level.
CMOS (Complementary Metal-Oxide-Semiconductor)
This is modern technology (74HCxx series, and all current microcontrollers), based on MOSFET transistors.
- Flexible supply voltage: 5V, 3.3V, 1.8V…
- Nearly zero static consumption.
- They have different thresholds than TTL.
Voltage Thresholds ( )
To understand compatibility, we need to look at four parameters in any chip’s datasheet:
The TTL case (5V)
- Logic 0: From 0V to 0.8V.
- Logic 1: From 2.0V to 5V.
- Dead zone: From 0.8V to 2.0V.
For a TTL chip, 2.5V is a perfect “1”. This is a huge advantage for compatibility, as we will see later.
The CMOS case (5V)
CMOS families are stricter and depend on the supply voltage (
- Logic 0: From 0V to approx 1.5V (
). - Logic 1: From approx 3.5V to 5V (
). - Dead zone: From 1.5V to 3.5V.
The Conflict: Connecting 5V and 3.3V
Here comes the real problem. You have an Arduino UNO (5V) and a sensor or a Raspberry Pi (3.3V).
Scenario A: 3.3V output 5V input
The 3.3V sensor sends data to the 5V Arduino. The 3.3V device will output about 3.2V when it wants to signal a “1”.
- If the input is TTL (or compatible): Yes. Since 3.2V is greater than 2.0V, it sees it as a solid HIGH.
- If the input is strict CMOS (
): No. 3.2V falls into the dead zone. It might work, but it’s unstable. - Arduino case: The ATmega328P has a
of . So 3.3V is sufficient to be a HIGH.
Verdict: Generally works directly, although it’s a bit tight.
Scenario B: 5V output → 3.3V input 🚨
The 5V Arduino sends data to the 3.3V Raspberry Pi or ESP32. The Arduino outputs 5V to signal a “1”.
The receiving device operates at 3.3V. Its internal protection diodes cannot handle more than 3.6V.
- Result: You fry the input pin or the entire processor of the 3.3V device.
- Exception: Some pins on modern microcontrollers are “5V tolerant”, but when in doubt, assume they are not.
Level Shifting
How do we safely connect the Arduino (5V) to the ESP32 (3.3V)?
The cheap solution for slow signals. We use two resistors (
Typical values:
For fast and bidirectional protocols like I2C, a voltage divider won’t work. We use a level shifter module (based on a BSS138 MOSFET).
- It has an HV side (High Voltage, 5V).
- It has an LV side (Low Voltage, 3.3V).
- Translates automatically in both directions.
We covered this in its own entry