In this tutorial, you will learn to use the onboard Bluetooth on Raspberry Pi Pico W using MicroPython to communicate with an Android app. The steps in this guide describe how to read the status of a pushbutton and send commands to control an LED using an Android app.
Raspberry Pi Pico W has the Infineon CYW4343 chip onboard, which features a single-band 2.4 GHz Wi-Fi 4 (802.11n) and Bluetooth 5.1. MicroPython modules will help us to easily interface with this chip and communicate with other devices.
The Infineon chip on Raspberry Pi Pico W also has Wi-Fi capability which is discussed in our article Connect Raspberry Pi Pico W to the Internet Using Wi-Fi.
- A Raspberry Pi Pico W.
- A pushbutton switch.
- Breadboard and jumper wires.
- USB cable to connect Pico.
Overview of Bluetooth Low Energy(BLE) On Pico W
Bluetooth Low Energy, abbreviated as BLE, is a variant of Bluetooth wireless technology developed with power-saving as its notable feature. Unlike Bluetooth Classic which is always active, BLE can stay in a sleep state when it is not transceiving data. This makes it ideal for use in battery-operated devices such as smartwatches, fitness trackers, and security monitoring devices.
If you would like to use Bluetooth Classic with Raspberry Pi Pico, we have a tutorial where we interface the HC-05 Bluetooth module with RPi Pico.
BLE Communication Topologies
BLE has the following communication topologies:
- Point-to-Point: Communication between two nodes(devices) in a network; server-client communication.
- Mesh Network: Multiple nodes can communicate to each other in this topology.
- Broadcast Mode: A server will broadcast data on the network that can be read by many devices.
There are many layers in the BLE architecture. The Link Layer in BLE defines certain states when a device communicates.
- Standby: The device neither transmits nor receives data.
- Initiating: Responds to advertising packets from a particular device to request a
- Advertising: Transmits advertising packets and processes packets sent in
response to advertising packets by other devices.
- Connection: In a connection with another device.
- Scanning: Listening for advertising packets from other devices.
- Isochronous Broadcasting: Broadcasts isochronous data packets.
- Synchronization: Listens for periodic advertising belonging to a specific advertising train transmitted by a device.
When in the connection state, a device can be in one of the two roles – the Central role or the Peripheral role. The device that will initiate a connection and transition from the Initiating state to the Connection state assumes the Central role. The device in the Peripheral role will be initially in the Advertising state. When it accepts a connection request from the Central device, it transitions to the Connection state.
In this guide, we will set up Raspberry Pi Pico in the peripheral role and establish point-to-point communication with an Android device via Bluetooth Low Energy.
Steps To Control LED Using Raspberry Pi Pico W Bluetooth Onboard
The following steps will demonstrate how we can control the onboard LED of Raspberry Pi Pico W using data received over Bluetooth from an Android application. Serial communication is emulated using BLE modules in this example.
Step 1: Install the MicroPython UF2 file
First, ensure that you have the correct version of the MicroPython UF2 file installed on Raspberry Pi Pico. You can find the UF2 file on the Raspberry Pi MicroPython documentation page. Download the correct UF2 file for Raspberry Pi Pico W with Wi-Fi and BLE support.
You can also find the latest builds on the MicroPython download page for Raspberry Pi Pico W.
After you have downloaded the file, connect your Raspberry Pi Pico to your computer using a USB cable while pressing the BOOTSEL button. A new drive named RPI-RP2 will be visible in your file explorer. Drag and drop the UF2 file to this drive. When done, the drive will automatically disappear from File Explorer. For more information, visit our getting started guide for Raspberry Pi Pico W.
Step 2: Save Bluetooth Modules to Pico W
We will need to save two MicroPython modules in our RPi Pico to communicate via Bluetooth Low Energy. I will describe the procedure using the Thonny IDE, but you can use other IDEs such as uPyCraft.
Connect your Raspberry Pi Pico W to the computer using a USB cable.
Open a new project in Thonny IDE by going to File>New. Copy the paste the code below.
# Helpers for generating BLE advertising payloads. from micropython import const import struct import bluetooth # Advertising payloads are repeated packets of the following form: # 1 byte data length (N + 1) # 1 byte type (see constants below) # N bytes type-specific data _ADV_TYPE_FLAGS = const(0x01) _ADV_TYPE_NAME = const(0x09) _ADV_TYPE_UUID16_COMPLETE = const(0x3) _ADV_TYPE_UUID32_COMPLETE = const(0x5) _ADV_TYPE_UUID128_COMPLETE = const(0x7) _ADV_TYPE_UUID16_MORE = const(0x2) _ADV_TYPE_UUID32_MORE = const(0x4) _ADV_TYPE_UUID128_MORE = const(0x6) _ADV_TYPE_APPEARANCE = const(0x19) # Generate a payload to be passed to gap_advertise(adv_data=...). def advertising_payload(limited_disc=False, br_edr=False, name=None, services=None, appearance=0): payload = bytearray() def _append(adv_type, value): nonlocal payload payload += struct.pack("BB", len(value) + 1, adv_type) + value _append( _ADV_TYPE_FLAGS, struct.pack("B", (0x01 if limited_disc else 0x02) + (0x18 if br_edr else 0x04)), ) if name: _append(_ADV_TYPE_NAME, name) if services: for uuid in services: b = bytes(uuid) if len(b) == 2: _append(_ADV_TYPE_UUID16_COMPLETE, b) elif len(b) == 4: _append(_ADV_TYPE_UUID32_COMPLETE, b) elif len(b) == 16: _append(_ADV_TYPE_UUID128_COMPLETE, b) # See org.bluetooth.characteristic.gap.appearance.xml if appearance: _append(_ADV_TYPE_APPEARANCE, struct.pack("<h", appearance)) return payload def decode_field(payload, adv_type): i = 0 result =  while i + 1 < len(payload): if payload[i + 1] == adv_type: result.append(payload[i + 2 : i + payload[i] + 1]) i += 1 + payload[i] return result def decode_name(payload): n = decode_field(payload, _ADV_TYPE_NAME) return str(n, "utf-8") if n else "" def decode_services(payload): services =  for u in decode_field(payload, _ADV_TYPE_UUID16_COMPLETE): services.append(bluetooth.UUID(struct.unpack("<h", u))) for u in decode_field(payload, _ADV_TYPE_UUID32_COMPLETE): services.append(bluetooth.UUID(struct.unpack("<d", u))) for u in decode_field(payload, _ADV_TYPE_UUID128_COMPLETE): services.append(bluetooth.UUID(u)) return services def demo(): payload = advertising_payload( name="micropython", services=[bluetooth.UUID(0x181A), bluetooth.UUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")], ) print(payload) print(decode_name(payload)) print(decode_services(payload)) if __name__ == "__main__": demo()Code language: Python (python)
Click on File> Save as and then select Raspberry Pi Pico.
Give the file name ble_advertising.py and press OK.
Repeat the procedure and save the code below as ble_simple_peripheral.py to your Pico W.
# This example demonstrates a UART periperhal. import bluetooth import random import struct import time from ble_advertising import advertising_payload from micropython import const _IRQ_CENTRAL_CONNECT = const(1) _IRQ_CENTRAL_DISCONNECT = const(2) _IRQ_GATTS_WRITE = const(3) _FLAG_READ = const(0x0002) _FLAG_WRITE_NO_RESPONSE = const(0x0004) _FLAG_WRITE = const(0x0008) _FLAG_NOTIFY = const(0x0010) _UART_UUID = bluetooth.UUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E") _UART_TX = ( bluetooth.UUID("6E400003-B5A3-F393-E0A9-E50E24DCCA9E"), _FLAG_READ | _FLAG_NOTIFY, ) _UART_RX = ( bluetooth.UUID("6E400002-B5A3-F393-E0A9-E50E24DCCA9E"), _FLAG_WRITE | _FLAG_WRITE_NO_RESPONSE, ) _UART_SERVICE = ( _UART_UUID, (_UART_TX, _UART_RX), ) class BLESimplePeripheral: def __init__(self, ble, name="mpy-uart"): self._ble = ble self._ble.active(True) self._ble.irq(self._irq) ((self._handle_tx, self._handle_rx),) = self._ble.gatts_register_services((_UART_SERVICE,)) self._connections = set() self._write_callback = None self._payload = advertising_payload(name=name, services=[_UART_UUID]) self._advertise() def _irq(self, event, data): # Track connections so we can send notifications. if event == _IRQ_CENTRAL_CONNECT: conn_handle, _, _ = data print("New connection", conn_handle) self._connections.add(conn_handle) elif event == _IRQ_CENTRAL_DISCONNECT: conn_handle, _, _ = data print("Disconnected", conn_handle) self._connections.remove(conn_handle) # Start advertising again to allow a new connection. self._advertise() elif event == _IRQ_GATTS_WRITE: conn_handle, value_handle = data value = self._ble.gatts_read(value_handle) if value_handle == self._handle_rx and self._write_callback: self._write_callback(value) def send(self, data): for conn_handle in self._connections: self._ble.gatts_notify(conn_handle, self._handle_tx, data) def is_connected(self): return len(self._connections) > 0 def _advertise(self, interval_us=500000): print("Starting advertising") self._ble.gap_advertise(interval_us, adv_data=self._payload) def on_write(self, callback): self._write_callback = callback def demo(): ble = bluetooth.BLE() p = BLESimplePeripheral(ble) def on_rx(v): print("RX", v) p.on_write(on_rx) i = 0 while True: if p.is_connected(): # Short burst of queued notifications. for _ in range(3): data = str(i) + "_" print("TX", data) p.send(data) i += 1 time.sleep_ms(100) if __name__ == "__main__": demo()Code language: Python (python)
You will also find the code for the two modules shown above on this GitHub page of MicroPython Bluetooth examples.
Step:3 MicroPython Bluetooth Example Code To Control LED
Now paste the following code into a new project on Thonny IDE.
# Import necessary modules from machine import Pin import bluetooth from ble_simple_peripheral import BLESimplePeripheral # Create a Bluetooth Low Energy (BLE) object ble = bluetooth.BLE() # Create an instance of the BLESimplePeripheral class with the BLE object sp = BLESimplePeripheral(ble) # Create a Pin object for the onboard LED, configure it as an output led = Pin("LED", Pin.OUT) # Initialize the LED state to 0 (off) led_state = 0 # Define a callback function to handle received data def on_rx(data): print("Data received: ", data) # Print the received data global led_state # Access the global variable led_state if data == b'toggle\r\n': # Check if the received data is "toggle" led.value(not led_state) # Toggle the LED state (on/off) led_state = 1 - led_state # Update the LED state # Start an infinite loop while True: if sp.is_connected(): # Check if a BLE connection is established sp.on_write(on_rx) # Set the callback function for data receptionCode language: Python (python)
Run your code by pressing F5 or by clicking on the Run icon. Save it to your Raspberry Pi Pico. Name the script main.py or any other name with a “.py” extension. Code saved as main.py will run automatically when Pico W reboots.
When the script runs, you will see the text “Starting advertising” displayed on the console.
Note: If the IDE shows you an error that the
bluetooth module cannot be imported or does not exist, you may have installed the wrong UF2 file to your Pico W.
Step 4: Setting Up Android App
Install the app Serial Bluetooth Terminal from the Play Store which is available for free. Open the app and from the navigation drawer, click on Devices.
Select the Bluetooth LE tab and tap on SCAN. You must now see your device listed as mpy-uart. Select the device name to connect to it.
When the connection is established, you can see the message “Connected” on the Android app. The console in Thonny IDE will also display the text “New connection 64”
Step 5: Toggle the Onboard LED Via Bluetooth App
On the Android app, type “toggle” without the quotes and tap the send icon.
The onboard LED on Raspberry Pi Pico W must change its state from OFF to ON. Sending the command again will toggle its state from ON to OFF.
Read Pushbutton Status Using Bluetooth on Raspberry Pi Pico W
The section above demonstrated how we can send data from an Android app to Pico. Now, this part will deal with receiving data on an Android via Bluetooth from Raspberry Pi Pico W. As an example, we will monitor if a pushbutton is pressed and display its status on the console of an Android app.
Step 1: Connect Pushbutton with Raspberry Pi Pico W
Connect a pushbutton between GPIO 0 and any of the GND pins of Raspberry Pi Pico W as shown in the schematic below.
Step 2: MicroPython Example Code To Read Data Over Bluetooth LE
The MicroPython modules, ble_advertising.py and ble_simple_peripheral.py, that we discussed above also need to be preloaded to your RPi Pico W.
Connect Pico W to your computer and upload the following code:
# Import necessary modules from machine import Pin import bluetooth from ble_simple_peripheral import BLESimplePeripheral import time # Create a Bluetooth Low Energy (BLE) object ble = bluetooth.BLE() # Create an instance of the BLESimplePeripheral class with the BLE object sp = BLESimplePeripheral(ble) # Set the debounce time to 0. Used for switch debouncing debounce_time=0 # Create a Pin object for Pin 0, configure it as an input with a pull-up resistor pin = Pin(0, Pin.IN, Pin.PULL_UP) while True: # Check if the pin value is 0 and if debounce time has elapsed (more than 300 milliseconds) if ((pin.value() is 0) and (time.ticks_ms()-debounce_time) > 300): # Check if the BLE connection is established if sp.is_connected(): # Create a message string msg="pushbutton pressed\n" # Send the message via BLE sp.send(msg) # Update the debounce time debounce_time=time.ticks_ms()Code language: Python (python)
Run your code by pressing F5 or by clicking on the Run icon. Save it to your Raspberry Pi Pico and name the script main.py or any other name with a “.py” extension.
When the script runs, you will see the text “Starting advertising” displayed on the console.
Step 3: Connect To Android App & Read Data
Similar to the procedure described in the LED controlling section above, we will use the Serial Bluetooth Terminal app. Connect your Android app to Pico W via BLE.
When you press the pushbutton connected to Pico W, you must now see the text “pushbutton pressed” on the console of the Android app.
In this Raspberry Pi Pico W Bluetooth guide, we saw how we can send and receive data from an Android app over BLE. You can use this guide to make various projects such as Android app-controlled home lights, Bluetooth-controlled toy cars, remotely control Neopixel LEDs, etc.
Hope you found the article useful. Please leave your thoughts and queries in the comments below.