Watch the associated video here:

How To Use Bluetooth On Raspberry Pi Pico With MicroPython

Hello fellow Makers! Today, we’re going to learn how to use Bluetooth on the Raspberry Pi Pico using MicroPython.

Back in mid-June this year, the Raspberry Pi team announced that the Bluetooth functionality is now available for Raspberry Pi Pico. Exciting, isn’t it?

We’ll upgrade our firmware, and create two programs; one for the remote control and one for the robot itself.

I’ve used the BurgerBot robot as a platform for experimenting with bluetooth, and you can learn how to build your own using with the information in the link provided.

Understanding Bluetooth Basics

Before we get started, let’s dive into some Bluetooth basics. Bluetooth is a wireless communication technology used to exchange data over short distances. Invented by Ericsson in 1989, it was intended to replace RS-232 data cables to create wireless communication between devices.

Bluetooth operates between 2.4 and 2.485 GHz in the ISM Band, and typically has a range of up to a hundred meters. It’s ideal for creating personal area networks for devices such as smartphones, PCs, peripherals, and even for controlling robots.

BLE Slide

Types of Bluetooth Technologies

There are two different types of Bluetooth technologies:

  1. Classic Bluetooth or Human Interface Devices (HID): This is used for devices like keyboards, mice, and game controllers. It allows users to control the functionality of their device from another device over Bluetooth.

  2. Bluetooth Low Energy (BLE): A newer, power-efficient version of Bluetooth, it’s designed for short bursts of long-range radio connections, making it ideal for Internet of Things applications where power consumption needs to be kept to a minimum.

BLE Slide

Step 1: Updating the Firmware

To access this new functionality, all we need to do is update the firmware on our Raspberry Pi Pico. This can be done either using an updater or by downloading the file from and dragging it onto our Pico from the explorer or Finder window.

Step 2: Establishing a Bluetooth Connection

A Bluetooth connection goes through a series of different stages. First, we need to advertise a service on the server (in our case, the Raspberry Pi Pico). Then, on the client side (the robot, for instance), we need to scan for any remote control nearby. Once it’s found one, we can then establish a connection.

Remember, you can only have one connection at a time with Raspberry Pi Pico’s implementation of Bluetooth in MicroPython. After the connection is established, we can transfer data (up, down, left, right commands to our robot). Once we’re done, we can disconnect.

BLE Slide

Step 3: Implementing GATT (Generic Attribute Profiles)

GATT, or Generic Attribute Profiles, is used to establish the communication between two devices. However, it’s only used once we’ve established the communication, not at the advertising and scanning stage.

To implement GATT, we will need to use asynchronous programming. In asynchronous programming, we don’t know when a signal is going to be received from our server to move the robot forward, left, or right. Therefore, we need to use asynchronous code to handle that, to catch it as it comes in.

There are three essential commands in asynchronous programming:

  1. async: Used to declare a function as a coroutine.

  2. await: Used to pause the execution of the coroutine until the task is completed.

  3. run: Starts the event loop, which is necessary for asynchronous code to run.

BLE Slide

BLE Slide

Step 4: Write Asynchronous Code

There is a module in Python and MicroPython that enables asynchronous programming, this is the asyncio (or uasyncio in MicroPython).

We can create special functions that can run in the background, with multiple tasks running concurrently. (Note they don’t actually run concurrently, but they are switched between using a special loop when an await call is used). These functions are called coroutines.

Remember, the goal of asynchronous programming is to write non-blocking code. Operations that block things, like input/output, are ideally coded with async and await so we can handle them and have other tasks running elsewhere.

The reason I/O (such as loading a file or waiting for a user input are blocking is because they wait for the thing to happen and prevent any other code from running during this waiting time).

It’s also worth noting that you can have coroutines that have other coroutines inside them. Always remember to use the await keyword when calling a coroutine from another coroutine.

The code

I’ve uploaded the working code to Github Gists so you can understand whats going on.

To use this code:

  1. Upload the robot code to the robot and rename it to - this will ensure it runs when the Pico is powered up.
  2. Upload the remote code to the remote pico and rename it to

The picos should flash quickly when not connected, and slowly once the connection is established.

Did you find this content useful?

If you found this high quality content useful please consider supporting my work, so I can continue to create more content for you.

I give away all my content for free: Weekly video content on YouTube, 3d Printable designs, Programs and Code, Reviews and Project write-ups, but 98% of visitors don't give back, they simply read/watch, download and go. If everyone who reads or watches my content, who likes it, helps fund it just a little, my future would be more secure for years to come. A price of a cup of coffee is all I ask.

There are a couple of ways you can support my work financially:

If you can't afford to provide any financial support, you can also help me grow my influence by doing the following:

Thank you again for your support and helping me grow my hobby into a business I can sustain.
- Kevin McAleer