To interface a Raspberry Pi Pico (RP2040) and an Arduino Micro (ATmega32U4) via UART, you must use a bidirectional logic level converter (like the BSS138). The Pico operates strictly at 3.3V logic, while the standard Arduino Micro uses 5V logic. Connecting the Micro's 5V TX pin directly to the Pico's 3.3V RX pin will instantly destroy the RP2040 silicon through overvoltage latch-up. This guide walks through the exact pinouts, level-shifting wiring, and compilable C++ code to bridge these two distinct ecosystems safely.

Spec-Sheet Showdown: Raspberry Pi Pico and Arduino Micro

Before wiring them together, it is critical to understand the hardware asymmetry between these two boards. The Pico is a modern, high-clock-speed microcontroller with abundant memory, while the Micro is an older 8-bit AVR chip prized for its native USB HID capabilities. Below is the data-dense hardware comparison to inform your architecture decisions.

Feature Raspberry Pi Pico (RP2040) Arduino Micro (ATmega32U4)
Core / Architecture Dual-core ARM Cortex-M0+ 8-bit AVR
Clock Speed 133 MHz (overclockable to 250+ MHz) 16 MHz
Flash / SRAM 2 MB Flash / 264 KB SRAM 32 KB Flash / 2.5 KB SRAM
Logic Voltage 3.3V (Absolute max 3.6V on GPIO) 5V (Absolute max 5.5V on GPIO)
Hardware UARTs 2 (UART0, UART1) 1 (Serial1 on pins 0/1)
USB Interface USB 1.1 Controller (Host/Device) Native USB (Directly on MCU pins)
Typical 2026 Price $4.00 (Standard) / $6.00 (Pico W) $18.50 (Genuine) / $7.00 (Clone)

Parts List and UART Pin Mapping

Because of the voltage mismatch, a simple TX-to-RX crossover will not work. You need a dedicated logic level shifting module. Avoid resistor voltage dividers for UART; they lack the speed and edge sharpness required for reliable 115200 baud serial communication.

Required Components

  • Master: Raspberry Pi Pico (RP2040, standard variant with pre-soldered headers) - Targeted in code below.
  • Slave: Arduino Micro (ATmega32U4, 5V/16MHz variant) - Targeted in code below.
  • Level Shifter: BSS138 Bidirectional Logic Level Converter (4-channel breakout board).
  • Wiring: 22 AWG solid-core jumper wires and a half-size breadboard.

UART Pin Mapping Table

This table maps the physical connections through the BSS138 level shifter. The BSS138 has a Low Voltage (LV) side and a High Voltage (HV) side. Ensure the Pico connects only to the LV side, and the Micro connects only to the HV side.

Pico (3.3V) Pin BSS138 LV Side BSS138 HV Side Micro (5V) Pin Signal Direction
GP4 (UART1 TX) LV1 HV1 RX (Pin 0) Pico to Micro
GP5 (UART1 RX) LV2 HV2 TX (Pin 1) Micro to Pico
3V3(OUT) (Pin 36) LV - - LV Reference
- - HV 5V Pin HV Reference
GND GND GND GND Common Ground

Step-by-Step Wiring and Logic Level Shifting

WARNING: Never power the BSS138 HV side from the Pico's VBUS (5V) if the Pico is powered via USB, as this can cause ground loops. Use the Micro's 5V output to power the HV side of the level shifter, and the Pico's 3V3 output to power the LV side.
  1. Establish Common Ground: Connect a 22 AWG black wire from any Pico GND pin to the GND rail on your breadboard. Connect the Micro's GND pin to the same rail. Finally, jumper the breadboard GND rail to both the LV GND and HV GND pins on the BSS138 module.
  2. Power the Level Shifter: Connect the Pico's 3V3(OUT) pin to the BSS138 'LV' pin. Connect the Micro's 5V pin to the BSS138 'HV' pin. Verify with a multimeter that you read exactly 3.3V across LV-GND and 5.0V across HV-GND before proceeding.
  3. Wire the TX Line (Pico to Micro): Run a wire from Pico GP4 to BSS138 LV1. Run a second wire from BSS138 HV1 to Arduino Micro Pin 0 (RX).
  4. Wire the RX Line (Micro to Pico): Run a wire from Pico GP5 to BSS138 LV2. Run a second wire from BSS138 HV2 to Arduino Micro Pin 1 (TX).
  5. Verify Crossover: Visually trace the lines. The TX pin on the Master must ultimately hit the RX pin on the Slave, and vice versa. The level shifter handles the voltage translation, but it does not swap the data direction.

Complete Compilable UART Bridge Code

The following code targets the Raspberry Pi Pico (RP2040) using the Earle Philhower Arduino core, and the Arduino Micro (ATmega32U4) using the standard Arduino AVR core. Both must be compiled and uploaded via their respective USB connections before testing the UART bridge.

Pico Master Code (Sensor Polling and Transmission)

This code reads an analog sensor on GP26, formats a payload, and transmits it via UART1. It includes a timeout error handler to prevent the main loop from hanging if the serial buffer locks.

// Target: Raspberry Pi Pico (RP2040) via Earle Philhower Core
#include 

#define UART_TX_PIN 4
#define UART_RX_PIN 5
#define SENSOR_PIN 26
#define BAUD_RATE 115200

unsigned long lastSend = 0;
const unsigned long sendInterval = 500; // ms

void setup() {
  // Initialize USB Serial for debug monitor
  Serial.begin(115200);
  while (!Serial && millis() < 3000) { delay(10); }
  
  // Configure Hardware UART1
  Serial1.setTX(UART_TX_PIN);
  Serial1.setRX(UART_RX_PIN);
  Serial1.begin(BAUD_RATE);
  
  pinMode(SENSOR_PIN, INPUT);
  Serial.println("Pico Master Initialized.");
}

void loop() {
  if (millis() - lastSend >= sendInterval) {
    lastSend = millis();
    
    int rawSensor = analogRead(SENSOR_PIN);
    float voltage = rawSensor * (3.3 / 1023.0);
    
    // Format payload with start/stop markers for reliable parsing
    char payload[32];
    snprintf(payload, sizeof(payload), "\n", voltage);
    
    Serial1.print(payload);
    Serial.print("Sent: ");
    Serial.print(payload);
  }
  
  // Non-blocking read for any acknowledgments from Micro
  if (Serial1.available()) {
    String ack = Serial1.readStringUntil('\n');
    if (ack.length() > 0) {
      Serial.print("Micro ACK: ");
      Serial.println(ack);
    }
  }
}

Micro Slave Code (Receiving and HID Prep)

This code listens on Serial1, parses the payload, and includes strict buffer overflow protection. The ATmega32U4 only has 2.5 KB of SRAM, so unhandled serial buffers will quickly cause a memory crash.

// Target: Arduino Micro (ATmega32U4) via Standard AVR Core
#include 

#define UART_RX_PIN 0
#define UART_TX_PIN 1
#define BAUD_RATE 115200
#define MAX_BUFFER 64

char receiveBuffer[MAX_BUFFER];
int bufferIndex = 0;

void setup() {
  Serial.begin(115200); // USB Debug
  Serial1.begin(BAUD_RATE); // Hardware UART on pins 0/1
  
  // Clear any garbage in the hardware buffer on boot
  while (Serial1.available()) { Serial1.read(); }
}

void loop() {
  while (Serial1.available()) {
    char c = Serial1.read();
    
    // Error Handling: Prevent buffer overflow
    if (bufferIndex >= MAX_BUFFER - 1) {
      Serial.println("ERR: Buffer Overflow, flushing.");
      bufferIndex = 0;
      while (Serial1.available()) { Serial1.read(); }
      break;
    }
    
    if (c == '\n') {
      receiveBuffer[bufferIndex] = '\0'; // Null terminate
      processPayload(receiveBuffer);
      bufferIndex = 0; // Reset for next packet
    } else {
      receiveBuffer[bufferIndex++] = c;
    }
  }
}

void processPayload(char* data) {
  // Verify start and stop markers
  if (data[0] == '<' && data[strlen(data)-2] == 'E') {
    float voltage = atof(&data[3]); // Skip '

Debugging: Exact Error Strings and the First Three Checks

When bridging two different architectures, serial communication is the first thing to fail. If your Serial Monitor is blank or throwing errors, follow this exact diagnostic path.

The First Three Things to Check When It Fails

  1. Common Ground Continuity: Disconnect power. Set your multimeter to continuity mode. Place one probe on the Pico's GND pin and the other on the Micro's GND pin. You must read less than 1 ohm. If it reads OL (open loop), your serial data has no return path and will fail.
  2. TX/RX Crossover Verification: Trace the physical wires. Pico GP4 (TX) must terminate at Micro Pin 0 (RX). If you wired TX-to-TX, the collision will result in total silence on the bus.
  3. Logic Level Voltages: Power the circuit. Measure the voltage on the BSS138 HV2 pin while the Micro is transmitting. You should see it toggling between 0V and ~5V. Measure LV2 (Pico side); it should toggle between 0V and ~3.3V. If LV2 stays at 5V, your level shifter is unpowered or blown.

Ranked Causes for Exact Error Strings

Error String: error: 'Serial1' was not declared in this scope

  • Cause 1 (Most Likely): You are compiling the Pico code using the official Arduino Mbed OS core instead of the Earle Philhower core. The Mbed core uses Serial2 or requires explicit UART instantiation. Switch your Board Manager to 'Raspberry Pi Pico/RP2040' by Earle Philhower.
  • Cause 2: You are compiling the Micro code but selected 'Arduino Leonardo' in the IDE without checking the pin definitions. While they share the ATmega32U4, some older IDE versions map hardware serial differently.

Error String: FatalError: Failed to connect to RP2040 (During Upload)

  • Cause 1: You accidentally selected the UART Serial port in the Arduino IDE Port menu instead of the USB UF2 port. The Pico uploads via USB mass storage (UF2), not via the UART pins you just wired.
  • Cause 2: The Pico is in a locked state. Hold the 'BOOTSEL' button on the Pico while plugging in the USB cable to force it into mass-storage upload mode.

Symptom: Serial monitor shows garbage characters (e.g., ÿÿÿ or random squares).

  • Cause 1: Baud rate mismatch. Ensure both Serial1.begin() calls are exactly 115200.
  • Cause 2: Missing common ground. The logic level shifter cannot translate voltages if the ground reference is floating.

Extending and Simplifying the Build

Depending on your project constraints, you may want to alter the hardware footprint of this dual-controller setup.

How to Simplify the Build

If you want to eliminate the BSS138 logic level converter entirely to save breadboard space and reduce wiring complexity, swap the Arduino Micro for a 3.3V / 8MHz Pro Micro clone. Many third-party manufacturers sell the ATmega32U4 Pro Micro in a 3.3V variant (typically priced around $5.00). Because both the Pico and the 3.3V Pro Micro operate at the same logic level, you can wire Pico GP4 directly to Pro Micro Pin 0, and Pico GP5 directly to Pro Micro Pin 1. Note: You must change the Micro's board definition in the IDE to 'SparkFun Pro Micro (3.3V, 8MHz)' to ensure the compiler sets the correct clock divisors for the baud rate generator.

How to Extend the Build

The primary reason to combine a Pico and an ATmega32U4 board is to split the workload based on silicon strengths.

  • USB HID Extension: Extend the Micro's code to include the Keyboard.h library. Let the Pico handle heavy I2C sensor polling (like a BNO055 IMU) and send quaternion data over UART to the Micro. The Micro can then translate that data into native USB mouse movements, bypassing the need for custom PC drivers.
  • Wireless Telemetry: Upgrade the Pico to a Raspberry Pi Pico W. Use the Pico to read the UART data from the Micro and publish it to an MQTT broker over WiFi, turning the wired Micro into a wireless IoT macro-pad or sensor node.

For authoritative reference on the RP2040 UART peripherals, consult the official Raspberry Pi Pico Datasheet. For the ATmega32U4 serial mapping, review the Arduino Micro hardware documentation. For a deeper understanding of MOSFET-based level shifting, refer to the SparkFun Logic Levels tutorial.