Raspberry Pi and Raspberry Pi Pico connected with wires

How to Connect a Raspberry Pi to a Pico (4 Methods Compared)

If you click our links and make a purchase, we may earn an affiliate commission. Learn more

Connecting a Raspberry Pi Pico to a Raspberry Pi can be a powerful combination, but it’s not as simple as it seems. There are many ways to do it (USB, UART, I2C, and even Wi-Fi), and it’s easy to get confused about which method to choose or how to make it work. I’ve tested all of them, so you don’t have to. Here’s what you need to know.

There are four main ways to connect a Raspberry Pi to a Pico: USB, UART (Serial), I2C, and Wi-Fi. Each method has specific requirements and use cases, but all can be used to send data between the two boards.

If you’re not sure where to start (or want something that works), this guide will walk you through each method with clear steps, example code, and practical tips to avoid common mistakes.

If you’re like me and sometimes mix up syntax between programming languages, I’ve got just the thing for you. I’ve put together a Python cheat sheet with all the essential syntax in one place, so you can keep it handy and avoid any confusion. Download it here for free!

Why Should You Connect a Pi and a Pi Pico?

The Raspberry Pi and the Raspberry Pi Pico can both handle many tasks, but they generally serve different purposes. You can review some of the differences in the “Differences with other models” section of the What is Raspberry Pi Pico? article and read more below.

Connecting the Pi and Pi Pico enables the benefits of each. Which one is better? Neither one, they are both the best!

It’s a case of “use the best tool for the job.” It depends on what you’re trying to accomplish and what the constraints are. Here are some of the fundamental differences:

Raspberry Pi

  • Bigger
  • >$55 (excluding SD)
  • Runs an OS (program in any language)
    • Networking built-in
    • High-res Displays
    • Gobs of storage (GB!)
  • Consumes 2 – 7 Watts1

Pi Pico / Pi Pico W

  • Smaller, can be tiny
  • $4 – $8
  • Runs C, C++, MicroPython, Rust, etc.
    • Networking is possible
    • Small displays are possible
    • Small storage, expandable
  • Consumes 0.2 – 0.3 Watts2

In short, the Raspberry Pi is a single-board computer (SBC) that can do almost anything a regular computer can do. You can program in many languages, hook up a mouse and keyboard, and connect it to sensors, but it all comes at a price. The cost, size and power consumption are much higher.

In contrast, the Raspberry Pi Pico is based on a microcontroller. Microcontrollers didn’t typically come with Wi-Fi or run at hundreds of MHz, or have much memory, but they have always been small, had lots of pins, and just taken small sips of power. It can do some of the same things the Pi can do, but it is not as easy to use.

So the reason to connect a Pi and a Pi Pico: you can combine the strengths of each!

Prefer reading without ads and popups?
Members get an ad-free version of every guide, plus exclusive project support.
Join the Community | Sign In

If you have many distributed sensors, each one could be monitored by a Pi Pico running on batteries and connected to a single Pi, which could consolidate the data while taking advantage of the plentiful storage, hosting a webpage and operating a computer monitor.

Prerequisites for All Examples

For all the examples below, you’ll get two scripts: one in Python for the Raspberry Pi, and one in MicroPython for the Pico. All you need is both boards and a few wires to get started.

To work through any of the examples, you’ll need to set up the Raspberry Pi and the Raspberry Pi Pico environment so you can upload the scripts.

If you already set up your Raspberry Pi Pico, you may want to make sure that you have a recent version installed since the I2C example requires 1.26 or later.

The Raspberry Pi will need Python and some libraries. Follow along in the example sections to install just the packages you need for that example.

Required Hardware

At a minimum, here is the hardware you need to try these examples:

Required Software

On the software part, you don’t need anything fancy. Here’s what I recommend using for this tutorial:

Set up for all Examples

Take a minute to review how to get started with the Raspberry Pi Pico.

There are several development environments for programming the Raspberry Pi Pico from a PC or the Raspberry Pi.

Thonny is already installed on the Raspberry Pi and is very capable. There is also a less user-friendly but powerful alternative called “mpremote.” It’s best for users who are comfortable in the terminal.

USB Communication From Pi to Pico

USB is a beast. Have you read the specification? Me neither! It’s hundreds of pages, possibly thousands by now. Fortunately, we don’t have to worry too much about how USB works.

The reason to start with USB communication is that it’s the standard way to communicate and program a Raspberry Pi Pico.

USB is a flexible interface; it can simulate a serial port, have a control interface, or support modes capable of high bandwidth adequate for transferring video. In this example, we’ll communicate over the virtual serial interface.

Set up and Requirements for Using a USB

MicroPython remote control (mpremote) is a useful utility for controlling the Raspberry Pi Pico. The mpremote tool can send commands to the MicroPython REPL and read back the responses, so there’s no need to write a main.py file.

Grab my Python cheat sheet!
If like me, you always mix the languages syntax, download my cheat sheet for Python here!
Download now

First, you’ll need to install MicroPython remote control (aka mpremote):
sudo apt install micropython-mpremote

A Basic Hello & Response Example using USB TTY

The Raspberry Pi Pico includes a built-in REPL Python prompt that we can interact with directly. For more complex code, you can upload it to the Raspberry Pi Pico to be executed during startup.

Raspberry Pi Demo

The commands are as simple as running mpremote and passing it commands to run on the Raspberry Pi Pico. The code simply instantiates the GPIO pin and sets the state or reads it back. Go ahead and try these examples:

# Turn on the LED
mpremote exec "from machine import Pin; Pin('LED').on()"

# Turn off the LED
mpremote exec "from machine import Pin; Pin('LED').off()"

# Query the LED's pin state (prints 0 or 1, 0=off, 1=on)
mpremote exec "from machine import Pin; print(Pin('LED').value())"

mpremote sends each command to the REPL prompt, waiting for it to complete, and then passing the response back to the terminal. Using this technique, these commands can be integrated into a script.

A More Capable Raspberry Pi Demo with “mpremote resume.”

The above solution is great for short-lived commands which don’t take many lines of code to get the job done. As a project grows, you’ll want to put many of the lines of code into a file. This can speed up the responses and maintain the state. Fortunately, mpremote can handle that as well using the “resume” argument.

Here’s an example of how to use mpremote:

  • Copy the code below to a “demo.py” file.
  • Run the code you just copied on the Raspberry Pi Pico:
    mpremote run demo.py
    • Now there is a variable named “led” and three functions have been defined.
    • Each call to mpremote normally issues a soft-reset, which cleans out the functions and led definitions unless you use the “resume” argument.
  • Turn the LED on:
    mpremote resume exec "turn_on()"
  • Turn the LED off:
    mpremote resume exec "turn_off()"
  • Find out the LED state:
    mpremote resume exec "ask()"

NOTE: If you run “mpremote exec” without the resume argument just one time, it will wipe out the environment. You’ll need to re-run “mpremote run demo.py” to set it up again.

from machine import Pin
led = Pin('LED')

def turn_on():
    led.on()

def turn_off():
    led.off()

def ask():
    print('on' if led.value() == 1 else 'off')

Serial Communication From Pi to Pico

Serial is a simple bi-directional wire interface over two wires. In reality, you need three wires: RX, TX and ground.

It’s so easy to use and dependable that people use it for debugging and backup communications to solve issues when the code doesn’t run as expected or when Ethernet fails.

Serial communication is a robust and flexible way to transfer commands and data.

It is foundational to many other standards, including RS232 and RS485, for travelling longer distances (at higher voltages) and for handling a network of devices.

The serial interface is implemented using a peripheral that generates a clock signal, the peripheral is called: universal asynchronous receiver/transmitter (a UART for short).

Set up and Requirements for Using Serial

The Raspberry Pi Pico has two UARTs, which can be assigned to different pins. Check out the full pinout diagram to see which pins are available. As for the Raspberry Pi, the pins are stuck at pins 8 and 10.

Since the Raspberry Pi has only one of these peripherals and has no way to move them between pins, we are forced to use the only two pins that support serial.

It is critical to pay attention when making connections. Device 1’s TX must connect to device 2’s RX, and vice versa.

It sounds simple enough, but I have not met an electrical engineer who has not mixed them up at least once in their lives (often on a PCB which has to be bodge-wired to solve it!).

Setting up the Raspberry Pi

Here’s how to enable the serial port using the raspi-config tool:

  • Launch the Raspberry Pi Software Configuration Tool:
    sudo raspi-config
  • Navigate to Interface Options -> Serial Port.
    raspi-config selecting Serial Interface
    When prompted “Would you like a login shell to be accessible over serial?,” answer No.
    Screen capture of prompt: Would you like a login shell to be accessible on the serial port?
  • When prompted “Would you like the serial port hardware to be enabled?,” answer Yes.
    Screen capture of raspi-config: Would you like the serial port hardware to be enabled?
  • Now quit out of the tool.
  • Restart your Raspberry Pi:
    sudo reboot
  • If it’s working, you should see a file here:
    ls /dev/serial0

Making the Wire Connections

DescriptionRaspberry PiRaspberry Pi Pico
UART pin from Pico to PiGPIO15 / RX / pin 10UART0 TX / pin 1
UART pin from Pi to PicoGPIO14 / TX / pin 8UART0 RX / pin 2
Groundground / pin 6ground / pin 3

A Basic LED Control Example using Serial

The Raspberry Pi Pico has an LED which can be controlled remotely using the serial port. The Raspberry Pi can send commands by passing an argument “on” or “off”. The speed or baud rate is set to 115200 bytes per second. If you like, you can set it to a different speed (it must be an allowed number, for example, 9600), as long as it’s set the same on both units.

Raspberry Pi Demo

This script takes two command-line arguments, “on” and “off”. The script will then open the serial port, and write “led_on” or “led_off” followed by a CR (carriage return) “\r”.

import serial
import sys


if len(sys.argv) != 2 or sys.argv[1] not in ('on', 'off'):
    print(f"Usage: {sys.argv[0]} [on|off]")
    sys.exit(1)

s1 = serial.Serial('/dev/serial0', baudrate=115200, timeout=.5)

# Write "led_on\r" to the serial port
if sys.argv[1] == 'on':
    s1.write(b'led_on\r')
# Write "led_off\r" to the serial port
elif sys.argv[1] == 'off':
    s1.write(b'led_off\r')

print(f"Response: {s1.read(20).decode()}")

sys.exit(0)

Raspberry Pi Pico Demo (install as main.py)

The Raspberry Pi Pico demo works by reading bytes out of the buffer and appending them to a buffer named buf. If there are any CR characters, extract the string that ends with the CR and pass it to the run_cmd function. In run_cmd, the value is compared to the COMMANDS dictionary, which maps strings to functions.

import time
from machine import UART, Pin


time.sleep(0.1) # Wait for USB to become ready

WAIT_UART_MS = 100

# , tx=Pin(4), rx=Pin(5),
uart0 = UART(0, baudrate=115200, timeout=WAIT_UART_MS)
led = Pin('LED', Pin.OUT, value=0)
buf = b''


def led_on():
    led.on()
    return b'on'


def led_off():
    led.off()
    return b'off'


COMMANDS = {
    b'led_on': led_on,
    b'led_off': led_off,
}


def run_cmd(cmd):
    """Call a function mapped to a string of value `cmd` if defined.

    Searches for a key matching `cmd` in the dictionary COMMANDS, calls the
    associated function and writes the result to uart0. If `cmd` is not a key
    in the dictionary, print an error to the console.

    Args:
      cmd: A string to look up in COMMANDS
    """
    if cmd in COMMANDS:
        print(f"Running {cmd:s}")
        response = COMMANDS[cmd]().decode()
        print(f"Sending {response:s}")
        uart0.write(response + '\r\n')
    else:
        print(f"Invalid Command: {cmd:s}")


while (True):
    # Read any characters available from the UART, wait up to WAIT_UART_MS ms
    chunk = uart0.read()

    # If there is nothing new, just start the loop over (uart0.read())
    if not chunk:
        continue

    # Append the new data to the buffer
    buf += chunk

    # Parse the buffer contents
    while True:
        # Search for a carriage return
        idx = buf.find(b'\r')
        if idx < 0:
            # If no CR, exit the loop early
            break
        # Get the substring from the beginning of the buffer up to the CR
        token = buf[:idx]
        # Update the buffer to remove the parsed token
        buf = buf[idx+1:]
        # Pass the token to the command runner
        run_cmd(token)
        break

References

I2C Communication From Pi to Pico

I2C is a two-wire protocol for connecting micro-controllers with peripherals. It has an addressing scheme to handle multiple peripherals and operates at relatively low speeds.

It is typically used to connect several chips within a single board, and sometimes between boards. Since we’re connecting two separate boards, we have to keep the wires short.

I2C is great for straightforward and reliable communications over short distances. There are two roles on an I2C bus: a controller/master which drives the bus by moving the clock line and making requests, and peripherals which each respond to an address.

Setup and Requirements for Using I2C

I2C requires pull-up resistors, but fortunately for us, the pull-up resistors are already integrated into the Raspberry Pi.

I2C devices have one of two roles: controller and peripheral (formerly called master and slave). Both the Raspberry Pi and Raspberry Pi Pico can take on either role, so in this case, the Raspberry Pi will be the controller (driving the clock and making reads and writes to addresses), and the Raspberry Pi Pico will be the peripheral (listening for its address).

Setting up the Raspberry Pi

Lost in the terminal? Grab My Pi Cheat-Sheet!
Download the free PDF, keep it open, and stop wasting time on Google.
Download now

Here’s how to enable I2C using raspi-config:

  • Launch the Raspberry Pi Software Configuration Tool:
    sudo raspi-config
  • Navigate to Interface Options.
  • Select “I2C”:
    Screen capture of raspi-config: select I2C
  • On the page asking “Would you like the ARM I2C interface to be enabled?”, select <Yes>.
    raspi-config: Enable I2C interface
  • Now quit out of the tool.
  • Reboot the Raspberry Pi:
    sudo reboot

Making the Wire Connections

DescriptionRaspberry PiRaspberry Pi Pico
I2C data pinGPIO2 / SDA / pin 3I2C0 SDA / pin 1
I2C clock pinGPIO3 / SCL / pin 5I2C0 SDA / pin 2
Groundground / pin 6ground / pin 3

A Basic LED Control Example using I2C

Raspberry Pi Demo

import smbus
import sys

if len(sys.argv) != 2 or sys.argv[1] not in ('on', 'off', '?'):
    print(f"Usage: {sys.argv[0]} [on|off|?]")
    sys.exit(1)

bus = smbus.SMBus(1)

if sys.argv[1] == 'on':
    bus.write_byte(0x43, 1)
    
elif sys.argv[1] == 'off':
    bus.write_byte(0x43, 0)
    
elif sys.argv[1] == '?':
    state = bus.read_byte(0x43)
    state_str = "on" if state != 0 else "off"
    print(f"State: {state_str}")

sys.exit(0)

Raspberry Pi Pico Demo (install as main.py)

from machine import I2CTarget, Pin

led = Pin('LED', Pin.OUT, value=0)

led.off()

# Define an IRQ handler that prints the event id and responds to reads/writes.
def irq_handler(i2c_target, buf=bytearray(1)):
    flags = i2c_target.irq().flags()
    # An I2C read: retrieve the value and send it out over I2C 
    if flags & I2CTarget.IRQ_ADDR_MATCH_READ:
        i2c_target.write(buf)
    # An I2C write: read the data value from I2C and store it in buf
    if flags & I2CTarget.IRQ_ADDR_MATCH_WRITE:
        i2c_target.readinto(buf)

        # Turn the LED on if the I2C byte just received was not 0
        if buf[0] != 0:
            led.on()
        else:
            led.off()

# Create the I2C target and register to receive all events.
i2c = I2CTarget(0, addr=0x43, scl=1, sda=0)
i2c.irq(irq_handler, trigger=I2CTarget.IRQ_ADDR_MATCH_READ | I2CTarget.IRQ_ADDR_MATCH_WRITE, hard=True)

References

Wi-Fi Communication From Pi to Pico (using a router)

Wi-Fi is an excellent way to transfer lots of data or to connect many units. The support for Wi-Fi on all the Raspberry Pi products has been a major selling point and makes it easy to spend less time running wires, and gives the option to move your project around or have a rugged, weather-sealed outdoor solution.

While Wi-Fi can be very fast, it can be flaky depending on what other hardware you’re using (as well as the neighbor’s interfering hardware!).

Set up and Requirements for Using Wi-Fi

There’s more than one way to set up the Wi-Fi. In most cases, you already have a Wi-Fi access point / Wi-Fi router, which provides your internet access and may even be how you typically connect to the Raspberry Pi.

This example assumes that you already have the Raspberry Pi connected to Wi-Fi and would like to connect to the Raspberry Pi Pico using the existing Wi-Fi network.

In order to connect to the Raspberry Pi Pico, you’ll need to find out its IP address and enter that into the script as the “ip_addr” value. The Wi-Fi router can tell you, or you can watch the Raspberry Pi Pico start up, and it will print the IP address when it connects to the Wi-Fi network.

Here’s how to get the IP address from the console as it starts up:

  • Launch Thonny.
  • Make sure it’s configured to work with “MicroPython (Raspberry Pi Pico)” by checking the status bar at the bottom of the window.
  • Press Ctrl-D to perform a soft-reset (or Run -> Send EOF / Soft Reboot).
    Thonny's Run menu
  • Read the console:
    MPY: soft reboot
    Connecting.Done
    IP address: 192.168.1.23

    Thonny connected
  • Quit if you’re done, or leave it running.
  • Optionally verify that the Raspberry Pi can ping by typing into a terminal:
    ping <ip address>
    and press Ctrl-C to cancel.
    ping to verify
  • Modify the Raspberry Pi script to set “192.168.1.23” (replace with your IP) as the ip_addr variable.

A Basic LED Control Example using Wi-Fi

This example code uses the existing Wi-Fi network in your home to communicate between the Raspberry Pi and Raspberry Pi Pico.

Raspberry Pi Demo

This example shows how to create a TCP connection with a server, send a command, and optionally read the response.

NOTE: This example will not work without modification. You must set the ip_addr variable. See the above setup instructions.

import socket
import sys

ip_addr = ""

if len(sys.argv) != 2 or sys.argv[1] not in ('on', 'off', '?'):
    print(f"Usage: {sys.argv[0]} [on|off|?]")
    sys.exit(1)

cmd = sys.argv[1].encode()

#
clientsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
clientsocket.settimeout(5)
clientsocket.connect((ip_addr, 8080))

clientsocket.send(cmd + b'\n')
    
if sys.argv[1] == '?':
    state_str = clientsocket.recv(5)
    print(f"State: {state_str.decode()}")

sys.exit(0)

Raspberry Pi Pico Demo (install as main.py)

This example shows how to connect the Raspberry Pi Pico to a Wi-Fi network, create a TCP server, listen for commands, then parse the command and optionally turn the LED on or off, and optionally return the current LED state.

NOTE: This example will not work without modification. You must define “ssid” and “password” variables correctly.

from machine import Pin
import network
import socket
import time

# Fill in your network name (aka ssid) and password:
ssid = ""
password = ""

# Set the WLAN adapter into "station" mode so it can connect to an access-point
n1 = network.WLAN(network.WLAN.IF_STA)
n1.active(True)

# Uncomment this line to see available networks:
# n1.scan()

# Connect to the network
n1.connect(ssid, password)

print('Connecting.', end='')
while not n1.isconnected():
    print('.', end='')
    time.sleep(1)
print('Done')

print(f'IP address: {n1.ifconfig()[0]}')

led = Pin('LED', Pin.OUT, value=0)
state = b'off'

# Create a TCP server on port 8080
addr = socket.getaddrinfo('0.0.0.0', 8080)[0][-1]
s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s1.bind(addr)
# Only accept one connection at a time
s1.listen(1)

while True:
    # Wait for a connection, store the client in cl
    cl, addr = s1.accept()
    print(f'connection from {addr}')

    # Read characters from the socket until a newline character is found 
    cmd = cl.readline().strip()

    # Check if the command is "on"
    if cmd == b'on':
        print('on')
        led.on()
        state = cmd
    # Check if the command is "off"
    elif cmd == b'off':
        print('off')
        led.off()
        state = cmd
    # Check if the command is "?"
    elif cmd == b'?':
        print(f'Send: {state:s}')
        # Send the state variable back over the socket
        cl.send(state)
        cl.send(b'\n')
    # Close the socket
    cl.close()

References


🛠 This tutorial doesn't work anymore? Report the issue here, so that I can update it!

Stuck on this project? Ask me or other Pi users in the RaspberryTips Community. We help each other out and you'll get answers quick. Join and fix it together.

Comparing Communication Types

It can feel a little overwhelming to have so many options, so it can help to narrow down options by comparing them side-by-side. Check out the table below to get a quick look at each communication method’s strengths and weaknesses.

Communication TypeWire CountThroughput (kbps)Range (m)DuplexComplexity
USB4 wires / 1 cable<12,0003fulllow to medium
Serial3 wires921<15fulllow
I2C3 wires<4000.5halflow
Wi-FiN/A80,00020halfmedium

Please note that the preceding table has very rough estimates to get you in the ballpark. Depending on other design choices, the speeds and the ranges may vary. For example, serial can run over 15-meter wires at 9600 bps, but probably not at 115200.

How to Decide Which One is Best

The more complex the solution, the more time you’re likely to spend trying to get it working or trying to figure out why it doesn’t work 100% of the time. For this reason, I like to choose the simplest solution which can get the job done.

If I can make it work with USB or serial communications, I know that it will be a rock-solid solution. If I have an issue with serial, it is relatively easy to hook up an oscilloscope or a logic analyzer and figure out what went wrong.

Wireless solutions are also very attractive options, but the range is not always as long as you may need it to be, and the performance can be inconsistent. Wi-Fi might be working fine for a while, but then, a neighbor might start streaming video, and your connection might drop out.

Whichever solution you pick, it’s good to know what other options are available when you hit a roadblock. The Raspberry Pi is a potent single-board computer, and the Raspberry Pi Pico is a Swiss army knife for connecting to hardware. When they are working together, the possibilities are endless!

Whenever you’re ready, here are other ways I can help you:

Test Your Raspberry Pi Level (Free): Not sure why everything takes so long on your Raspberry Pi? Take this free 3-minute assessment and see what’s causing the problems.

The RaspberryTips Community: Need help or want to discuss your Raspberry Pi projects with others who actually get it? Join the RaspberryTips Community and get access to private forums, exclusive lessons, and direct help.

Master your Raspberry Pi in 30 days: If you are looking for the best tips to become an expert on Raspberry Pi, this book is for you. Learn useful Linux skills and practice multiple projects with step-by-step guides.

Master Python on Raspberry Pi: Create, understand, and improve any Python script for your Raspberry Pi. Learn the essentials step-by-step without losing time understanding useless concepts.

You can also find all my recommendations for tools and hardware on this page.

Similar Posts