Skip to content

Raspberry Pi Pico W BLE: Temp in MicroPython | ShillehTek

April 26, 2026

Project Overview

Raspberry Pi Pico W Bluetooth Low Energy (BLE) with MicroPython: In this getting-started guide you will configure a Raspberry Pi Pico W (or Pico 2 W) as a BLE peripheral that streams its on-board temperature, then build a second Pico into a BLE central device that scans for, connects to, and reads from it using MicroPython and the aioble library.

  • Time: 60-90 minutes
  • Skill level: Intermediate
  • What you will build: A BLE peripheral that advertises a temperature characteristic and a BLE central client that reads it over Bluetooth Low Energy.
Raspberry Pi Pico W running a MicroPython Bluetooth Low Energy tutorial for streaming temperature
Raspberry Pi Pico W: Bluetooth Low Energy with MicroPython.

Parts List

From ShillehTek

  • Raspberry Pi Pico 2 W with Pre-Soldered Headers - you need two boards if you want to follow both the peripheral and the central device examples. One board is enough for the peripheral-only walkthrough.
  • BME280 - optional add-on sensor if you want to publish real environmental readings (humidity and pressure) instead of the on-board temperature.
  • SSD1306 OLED - optional display for showing readings on the central device without a computer.
  • LCD1602 - optional character display alternative for showing readings on the central device.

External

  • USB cable (Micro USB or USB-C, depending on your Pico revision) for programming
  • Computer with Thonny IDE installed and the latest MicroPython firmware flashed to your board
  • Smartphone with the nRF Connect for Mobile app from Nordic Semiconductor (Android or iOS)

Note: Bluetooth on the Raspberry Pi Pico requires a "W" board - either the Raspberry Pi Pico W or the newer Raspberry Pi Pico 2 W. Standard Pico and Pico 2 boards do not include the Infineon CYW43439 wireless chip and cannot follow this tutorial.

Step-by-Step Guide

Step 1 - Install the aioble Package

Goal: Get the recommended BLE library onto your Raspberry Pi Pico so the example code can run.

What to do: Connect your Pico W to your computer over USB and open Thonny IDE. From the menu bar choose Tools → Manage Packages…, type aioble into the search box, click the matching result, and press Install. Wait a few seconds for the package to land on the board.

Thonny IDE on a computer searching for the aioble BLE package for Raspberry Pi Pico W
Search for the aioble package inside Thonny's package manager.
Thonny IDE installing the aioble BLE package onto a Raspberry Pi Pico W
Click Install and wait for confirmation.

Expected result: aioble is installed on the Pico, and you can import aioble from any MicroPython script running on the board.

Step 2 - Pick the Service and Characteristic UUIDs

Goal: Decide which BLE service and characteristic the peripheral will expose.

What to do: The peripheral will publish the on-board temperature, so we will reuse the SIG-assigned Environmental Sensing Service together with its standard Temperature characteristic. To find the UUIDs, open the Bluetooth SIG Assigned Numbers document. Search for "Environmental Sensing" to find the service and the supported characteristics (including temperature).

Bluetooth SIG Environmental Sensing Service characteristic list used by Raspberry Pi Pico W BLE projects
Characteristics supported by the Environmental Sensing Service.

The Environmental Sensing Service short UUID is 0x181A:

Bluetooth SIG page showing Environmental Sensing Service UUID 0x181A for Raspberry Pi Pico W BLE
Service UUID for environmental sensing.

And the temperature characteristic UUID is 0x2A6E:

Bluetooth SIG page showing Temperature Characteristic UUID 0x2A6E for Raspberry Pi Pico W BLE
Characteristic UUID for temperature.

Expected result: You have both UUIDs noted: service 0x181A, characteristic 0x2A6E.

Step 3 - Code the Pico W as a BLE Peripheral

Goal: Run a script on the first Pico W that registers the GATT service, writes the on-board temperature into the temperature characteristic every second, and continuously advertises the device under the name RPi-Pico.

What to do: Copy the script below into Thonny and run it on your first Pico W. The on-board temperature comes from the Pico's internal sensor through the picozero package, so make sure that package is installed too.

Code:


# Based on the Random Nerd Tutorials BLE example for the Raspberry Pi Pico W
# Project reference: https://RandomNerdTutorials.com/raspberry-pi-pico-w-bluetooth-low-energy-micropython/

from micropython import const
import asyncio
import aioble
import bluetooth
import struct
from picozero import pico_temp_sensor

# org.bluetooth.service.environmental_sensing
_ENV_SENSE_UUID = bluetooth.UUID(0x181A)
# org.bluetooth.characteristic.temperature
_ENV_SENSE_TEMP_UUID = bluetooth.UUID(0x2A6E)
# org.bluetooth.characteristic.gap.appearance.xml
_ADV_APPEARANCE_GENERIC_THERMOMETER = const(768)

# How frequently to send advertising beacons.
_ADV_INTERVAL_MS = 250_000

# Register GATT server.
temp_service = aioble.Service(_ENV_SENSE_UUID)
temp_characteristic = aioble.Characteristic(
    temp_service, _ENV_SENSE_TEMP_UUID, read=True, notify=True
)
aioble.register_services(temp_service)

# Encode the temperature characteristic value (sint16, hundredths of a degree).
def _encode_temperature(temp_deg_c):
    return struct.pack("<h", int(temp_deg_c * 100))

# Read the on-board temperature and update the characteristic.
async def sensor_task():
    while True:
        temperature = pico_temp_sensor.temp
        temp_characteristic.write(_encode_temperature(temperature), send_update=True)
        print(temperature)
        await asyncio.sleep_ms(1000)

# Advertise the service. Pause advertising while a central is connected.
async def peripheral_task():
    while True:
        try:
            async with await aioble.advertise(
                _ADV_INTERVAL_MS,
                name="RPi-Pico",
                services=[_ENV_SENSE_UUID],
                appearance=_ADV_APPEARANCE_GENERIC_THERMOMETER,
            ) as connection:
                print("Connection from", connection.device)
                await connection.disconnected()
        except asyncio.CancelledError:
            print("Peripheral task cancelled")
        except Exception as e:
            print("Error in peripheral_task:", e)
        finally:
            await asyncio.sleep_ms(100)

# Run both tasks.
async def main():
    t1 = asyncio.create_task(sensor_task())
    t2 = asyncio.create_task(peripheral_task())
    await asyncio.gather(t1, t2)

asyncio.run(main())
    

This script is adapted from the official aioble temperature example.

Thonny IDE shell showing Raspberry Pi Pico W advertising a BLE Environmental Sensing service and printing temperature
Once the script is running, the Pico advertises and starts publishing temperature readings.

Expected result: The Thonny shell prints temperature values once a second, and the Pico continuously advertises a BLE device named RPi-Pico with the Environmental Sensing service.

Step 4 - Verify with the nRF Connect App

Goal: Confirm the peripheral works by reading its temperature characteristic from your phone before you write any client code.

What to do: Install nRF Connect for Mobile from Nordic Semiconductor on your Android or iOS device. Open the app and start scanning for nearby BLE devices.

nRF Connect for Mobile app icon used to test Raspberry Pi Pico W BLE characteristics
nRF Connect for Mobile is the easiest way to explore a BLE device.

You should see the RPi-Pico device appear in the scan list. Tap Connect.

nRF Connect scanning and listing the RPi-Pico BLE peripheral broadcast by a Raspberry Pi Pico W
The Pico shows up in the scan list under the name we set in code.

Once connected, expand the Environmental Sensing service to reveal the temperature characteristic. Use the arrow buttons to read the current value and enable notifications.

nRF Connect connected to Raspberry Pi Pico W showing the Environmental Sensing service and temperature characteristic
Reading the temperature characteristic exposed by the Pico.

The raw bytes are not very useful by themselves, so switch the format using the small icon next to the value. On iPhone the second icon on the left opens the format picker.

nRF Connect format icon used to decode raw BLE temperature characteristic bytes from a Raspberry Pi Pico W
The format icon lets you decode the raw characteristic bytes.

Pick Unsigned Int. The temperature is encoded as Celsius multiplied by 100, so a reading of 2417 means 24.17 °C.

nRF Connect changing the Raspberry Pi Pico W temperature characteristic display format to Unsigned Int
Decode the value as an unsigned integer to see a useful number.
nRF Connect showing live temperature values received from a Raspberry Pi Pico W BLE peripheral
Live temperature readings being sent every second from the Pico.

Expected result: nRF Connect shows updated temperature readings from the Pico W roughly every second. If you see this, the peripheral side of the project is working.

Step 5 - Code the Second Pico W as a BLE Central Device

Goal: Build a second Pico into a BLE client that scans for the RPi-Pico peripheral, connects to it, and prints the decoded temperature in the Thonny shell.

What to do: Connect your second Raspberry Pi Pico W (or 2 W) and copy the script below into a fresh Thonny instance pointed at that board.

Code:


# Based on the Random Nerd Tutorials BLE central device example for the Pico W
# Project reference: https://RandomNerdTutorials.com/raspberry-pi-pico-w-bluetooth-low-energy-micropython/

from micropython import const
import uasyncio as asyncio
import aioble
import bluetooth
import struct

# org.bluetooth.service.environmental_sensing
_ENV_SENSE_UUID = bluetooth.UUID(0x181A)
# org.bluetooth.characteristic.temperature
_ENV_SENSE_TEMP_UUID = bluetooth.UUID(0x2A6E)

# Name of the peripheral you want to connect to.
peripheral_name = "RPi-Pico"

# Decode the temperature characteristic encoding (sint16, hundredths of a degree).
def _decode_temperature(data):
    try:
        if data is not None:
            return struct.unpack("<h", data)[0] / 100
    except Exception as e:
        print("Error decoding temperature:", e)
    return None

async def find_temp_sensor():
    # Active scan for 5 seconds with a tight interval/window for fast detection.
    async with aioble.scan(5000, interval_us=30000, window_us=30000, active=True) as scanner:
        async for result in scanner:
            print(result.name())
            if result.name() == peripheral_name and _ENV_SENSE_UUID in result.services():
                return result.device
    return None

async def main():
    while True:
        device = await find_temp_sensor()
        if not device:
            print("Temperature sensor not found. Retrying...")
            await asyncio.sleep_ms(5000)
            continue

        try:
            print("Connecting to", device)
            connection = await device.connect()
        except asyncio.TimeoutError:
            print("Timeout during connection. Retrying...")
            await asyncio.sleep_ms(5000)
            continue

        async with connection:
            try:
                temp_service = await connection.service(_ENV_SENSE_UUID)
                temp_characteristic = await temp_service.characteristic(_ENV_SENSE_TEMP_UUID)
            except asyncio.TimeoutError:
                print("Timeout discovering services/characteristics. Retrying...")
                await asyncio.sleep_ms(5000)
                continue

            while True:
                try:
                    temp_data = await temp_characteristic.read()
                    if temp_data is not None:
                        temp_deg_c = _decode_temperature(temp_data)
                        if temp_deg_c is not None:
                            print("Temperature: {:.2f}".format(temp_deg_c))
                        else:
                            print("Invalid temperature data")
                    else:
                        print("Error reading temperature: None")
                except Exception as e:
                    print("Error in main loop:", e)
                    break
                await asyncio.sleep_ms(1000)

# Create the event loop and start the main task.
loop = asyncio.get_event_loop()
loop.create_task(main())

try:
    loop.run_forever()
except Exception as e:
    print('Error occurred: ', e)
except KeyboardInterrupt:
    print('Program Interrupted by the user')
    

Expected result: The script saved successfully on the second Pico, ready to run.

Step 6 - Run Two Thonny Instances at Once

Goal: Have both Picos running simultaneously so you can watch the conversation happen in two shells.

What to do: By default Thonny only allows a single instance. To open two at once, go to Tools → Options and uncheck Allow only single Thonny instance. Close Thonny so the change takes effect, then reopen it twice. Make sure each window is connected to the correct COM/USB port using the picker in the bottom right corner.

Thonny settings showing how to allow multiple instances for programming two Raspberry Pi Pico W boards
Disable the single-instance restriction in Thonny so you can run two boards in parallel.

Expected result: Two Thonny windows, each connected to a different Pico W on a different COM port.

Step 7 - Run Peripheral and Central Together

Goal: Watch the second Pico discover the first, connect, and print decoded temperature readings in real time.

What to do: First run the peripheral script on the first Pico W. Confirm it is advertising and printing temperature values. Then run the central device script in the second Thonny window on your second Pico W. The central will scan, find the RPi-Pico device, connect to it, discover the environmental sensing service, and start polling the temperature characteristic.

Thonny shell showing a Raspberry Pi Pico W BLE central device reading and printing temperature from a Pico W peripheral
The BLE client reading the temperature characteristic from the peripheral.

Expected result: The central device's Thonny shell prints lines like Temperature: 24.31 once a second, sourced from the other board over Bluetooth Low Energy.

Conclusion

You now have a working BLE setup on the Raspberry Pi Pico: one board acts as a peripheral that exposes an Environmental Sensing service, and a second board acts as a central device that discovers it and reads live temperature readings over Bluetooth Low Energy using MicroPython and aioble.

Want the exact parts used in this build? Grab them from ShillehTek.com. If you want help customizing this project or building something for your product, check out our IoT consulting services.