Two-Way Bluetooth Communication Between Raspberry Pi Picos Learn how to set up two-way Bluetooth communication between two Raspberry Pi Picos using MicroPython. 31 August 2024 7 minute read By Kevin McAleer Share this article on Table of Contents Why BLE and Raspberry Pi Pico W?Project OverviewPrerequisitesUnderstanding the Roles: Central vs. PeripheralUUIDs: The IdentifiersThe Code Breakdown1. Setting Up the Peripheral (Server)What’s Happening Here?2. Setting Up the Central (Client)What’s Happening?3. Sending and Receiving DataSending DataReceiving Data4. Encoding and Decoding MessagesRunning the CodeConclusion Tags: Raspberry Pi Pico Bluetooth Difficulty: beginner Category: raspberrypi pico micropython Code: https://www.github.com/kevinmcaleer/pico_two_way_bluetooth Home Blog Two way bluetooth communication between raspberry pi picos Two-Way Bluetooth Communication Between Raspberry Pi Picos Learn how to set up two-way Bluetooth communication between two Raspberry Pi Picos using MicroPython. 31 August 2024 | 7 minute read | By Kevin McAleer | Share this article on Video For every project I create, I often make a corresponding YouTube video. Sometimes, there might be more than one video for a single project. You can find these videos in this section. Explore more through this this dedicated video. Bluetooth Low Energy (BLE) is a powerful technology that enables devices to communicate wirelessly over short distances. In this tutorial, we’ll explore how to use BLE to establish communication between two Raspberry Pi Pico W devices using MicroPython. One device will act as a Central (client), and the other as a Peripheral (server). We’ll send messages from the central to the peripheral, which will respond back, demonstrating a simple yet effective communication system. Why BLE and Raspberry Pi Pico W? The Raspberry Pi Pico W is a microcontroller that comes with built-in Wi-Fi and BLE capabilities, making it perfect for IoT (Internet of Things) projects. BLE is especially suited for low-power, low-data-rate communications, ideal for connecting small devices like the Pico W. Project Overview In this project, we’ll: Set up one Raspberry Pi Pico W as a Peripheral (server). Set up another Raspberry Pi Pico W as a Central (client). Enable the central to send messages to the peripheral. Have the peripheral respond back to the central. We’ll use MicroPython, a lightweight version of Python designed to run on microcontrollers, to program our devices. Prerequisites Before we dive in, make sure you have: Two Raspberry Pi Pico W devices. MicroPython installed on both devices. Basic understanding of Python and asynchronous programming. Thonny or another IDE set up for programming the Pico W. Understanding the Roles: Central vs. Peripheral In BLE communication: Peripheral (Server): This device advertises itself and offers services that other devices can interact with. It waits for a central device to connect and can send data to the central. Central (Client): This device scans for peripherals, connects to them, and interacts with their services by reading, writing, or subscribing to notifications. UUIDs: The Identifiers In BLE, UUIDs (Universal Unique Identifiers) are used to uniquely identify services and characteristics. We define these at the start of our program: _SERVICE_UUID = bluetooth.UUID(0x1848) _CHARACTERISTIC_UUID = bluetooth.UUID(0x2A6E) These UUIDs ensure that the central and peripheral are communicating over the correct channels. The Code Breakdown Let’s walk through the key parts of the code that enable this BLE communication. 1. Setting Up the Peripheral (Server) The peripheral is responsible for advertising its presence and providing a characteristic that the central can interact with. async def run_peripheral_mode(): # Set up the Bluetooth service and characteristic ble_service = aioble.Service(BLE_SVC_UUID) characteristic = aioble.Characteristic( ble_service, BLE_CHARACTERISTIC_UUID, read=True, notify=True, write=True, ) aioble.register_services(ble_service) print(f"{BLE_NAME} starting to advertise") while True: async with await aioble.advertise( BLE_ADVERTISING_INTERVAL, name=BLE_NAME, services=[BLE_SVC_UUID], appearance=BLE_APPEARANCE) as connection: print(f"{BLE_NAME} connected to another device: {connection.device}") tasks = [ asyncio.create_task(send_data_task(connection, characteristic)), ] await asyncio.gather(*tasks) print(f"{IAM} disconnected") break What’s Happening Here? Service and Characteristic: We create a service with a characteristic that the central can read from, write to, and receive notifications from. Advertising: The peripheral starts advertising itself, waiting for the central to connect. Handling Connections: Once connected, it sets up tasks to handle sending and receiving data. 2. Setting Up the Central (Client) The central scans for the peripheral, connects to it, and sends data. async def run_central_mode(): # Start scanning for a device with the matching service UUID while True: device = await ble_scan() if device is None: continue print(f"device is: {device}, name is {device.name()}") try: print(f"Connecting to {device.name()}") connection = await device.device.connect() except asyncio.TimeoutError: print("Timeout during connection") continue print(f"{IAM} connected to {connection}") # Discover services async with connection: try: service = await connection.service(BLE_SVC_UUID) characteristic = await service.characteristic(BLE_CHARACTERISTIC_UUID) except (asyncio.TimeoutError, AttributeError): print("Timed out discovering services/characteristics") continue except Exception as e: print(f"Error discovering services {e}") await connection.disconnect() continue tasks = [ asyncio.create_task(receive_data_task(characteristic)), ] await asyncio.gather(*tasks) await connection.disconnected() print(f"{BLE_NAME} disconnected from {device.name()}") break What’s Happening? Scanning: The central scans for peripherals advertising the specific service UUID. Connecting: Once it finds the peripheral, it connects and discovers the available services and characteristics. Communication: The central then listens for data from the peripheral and can send responses back. 3. Sending and Receiving Data Sending Data The central sends messages to the peripheral: async def send_data_task(connection, characteristic): global message_count while True: message = f"{MESSAGE} {message_count}" message_count += 1 print(f"sending {message}") try: msg = encode_message(message) characteristic.write(msg) await asyncio.sleep(0.5) response = decode_message(characteristic.read()) print(f"{IAM} sent: {message}, response {response}") except Exception as e: print(f"writing error {e}") continue await asyncio.sleep(0.5) Receiving Data The peripheral receives messages and can respond: async def receive_data_task(characteristic): global message_count while True: try: data = await characteristic.read() if data: print(f"{IAM} received: {decode_message(data)}, count: {message_count}") await characteristic.write(encode_message("Got it")) await asyncio.sleep(0.5) message_count += 1 except asyncio.TimeoutError: print("Timeout waiting for data in {ble_name}.") break except Exception as e: print(f"Error receiving data: {e}") break 4. Encoding and Decoding Messages BLE communication requires data to be sent in a specific format (bytes). We use two simple functions to encode and decode messages: def encode_message(message): return message.encode('utf-8') def decode_message(message): return message.decode('utf-8') Running the Code Peripheral Device: Set IAM = "Peripheral" and run the code on one Pico W. Central Device: Set IAM = "Central" and run the code on the other Pico W. The devices will establish a connection, and you’ll see messages being sent from the central to the peripheral and responses coming back. Conclusion In this tutorial, we explored how to use MicroPython and BLE to enable two Raspberry Pi Pico W devices to communicate wirelessly. We set up one device as a central and the other as a peripheral, enabling a two-way communication system. This foundation can be expanded into more complex IoT applications, allowing devices to interact in real-time over BLE. Whether you’re building a simple remote control or a complex sensor network, understanding how to leverage BLE in your projects opens up a world of possibilities. Happy coding! Code View Code Repository on GitHub - https://www.github.com/kevinmcaleer/pico_two_way_bluetooth Liked this article? You might like these too. Gamepad 2 Picotamachibi 2 Pi-Apps Pi-Apps is a powerful tool that simplifies the process of installing, managing, and updating applications on your Raspberry Pi. With its user-friendly interface and extensive software library, Pi-Apps is an essential tool for Raspberry Pi enthusiasts and beginners alike. Raspberry Pi Time machine This is a simple project to setup a Raspberry Pi as a Time Machine backup server for your Mac. This will allow you to backup your Mac automatically to the Raspberry Pi over your local network. Ultimate Guide to Setting Up RetroPie on a Raspberry Pi 4 A comprehensive guide to setting up RetroPie on a Raspberry Pi 4 Build your own AI Assistant in Python To create our AI assistant, we require a Raspberry Pi computer. I am personally using a Raspberry Pi 4, but it can also work on a Raspberry Pi Zero.
Two-Way Bluetooth Communication Between Raspberry Pi Picos Learn how to set up two-way Bluetooth communication between two Raspberry Pi Picos using MicroPython. 31 August 2024 7 minute read By Kevin McAleer Share this article on Table of Contents Why BLE and Raspberry Pi Pico W?Project OverviewPrerequisitesUnderstanding the Roles: Central vs. PeripheralUUIDs: The IdentifiersThe Code Breakdown1. Setting Up the Peripheral (Server)What’s Happening Here?2. Setting Up the Central (Client)What’s Happening?3. Sending and Receiving DataSending DataReceiving Data4. Encoding and Decoding MessagesRunning the CodeConclusion Tags: Raspberry Pi Pico Bluetooth Difficulty: beginner Category: raspberrypi pico micropython Code: https://www.github.com/kevinmcaleer/pico_two_way_bluetooth
Bluetooth Low Energy (BLE) is a powerful technology that enables devices to communicate wirelessly over short distances. In this tutorial, we’ll explore how to use BLE to establish communication between two Raspberry Pi Pico W devices using MicroPython. One device will act as a Central (client), and the other as a Peripheral (server). We’ll send messages from the central to the peripheral, which will respond back, demonstrating a simple yet effective communication system. Why BLE and Raspberry Pi Pico W? The Raspberry Pi Pico W is a microcontroller that comes with built-in Wi-Fi and BLE capabilities, making it perfect for IoT (Internet of Things) projects. BLE is especially suited for low-power, low-data-rate communications, ideal for connecting small devices like the Pico W. Project Overview In this project, we’ll: Set up one Raspberry Pi Pico W as a Peripheral (server). Set up another Raspberry Pi Pico W as a Central (client). Enable the central to send messages to the peripheral. Have the peripheral respond back to the central. We’ll use MicroPython, a lightweight version of Python designed to run on microcontrollers, to program our devices. Prerequisites Before we dive in, make sure you have: Two Raspberry Pi Pico W devices. MicroPython installed on both devices. Basic understanding of Python and asynchronous programming. Thonny or another IDE set up for programming the Pico W. Understanding the Roles: Central vs. Peripheral In BLE communication: Peripheral (Server): This device advertises itself and offers services that other devices can interact with. It waits for a central device to connect and can send data to the central. Central (Client): This device scans for peripherals, connects to them, and interacts with their services by reading, writing, or subscribing to notifications. UUIDs: The Identifiers In BLE, UUIDs (Universal Unique Identifiers) are used to uniquely identify services and characteristics. We define these at the start of our program: _SERVICE_UUID = bluetooth.UUID(0x1848) _CHARACTERISTIC_UUID = bluetooth.UUID(0x2A6E) These UUIDs ensure that the central and peripheral are communicating over the correct channels. The Code Breakdown Let’s walk through the key parts of the code that enable this BLE communication. 1. Setting Up the Peripheral (Server) The peripheral is responsible for advertising its presence and providing a characteristic that the central can interact with. async def run_peripheral_mode(): # Set up the Bluetooth service and characteristic ble_service = aioble.Service(BLE_SVC_UUID) characteristic = aioble.Characteristic( ble_service, BLE_CHARACTERISTIC_UUID, read=True, notify=True, write=True, ) aioble.register_services(ble_service) print(f"{BLE_NAME} starting to advertise") while True: async with await aioble.advertise( BLE_ADVERTISING_INTERVAL, name=BLE_NAME, services=[BLE_SVC_UUID], appearance=BLE_APPEARANCE) as connection: print(f"{BLE_NAME} connected to another device: {connection.device}") tasks = [ asyncio.create_task(send_data_task(connection, characteristic)), ] await asyncio.gather(*tasks) print(f"{IAM} disconnected") break What’s Happening Here? Service and Characteristic: We create a service with a characteristic that the central can read from, write to, and receive notifications from. Advertising: The peripheral starts advertising itself, waiting for the central to connect. Handling Connections: Once connected, it sets up tasks to handle sending and receiving data. 2. Setting Up the Central (Client) The central scans for the peripheral, connects to it, and sends data. async def run_central_mode(): # Start scanning for a device with the matching service UUID while True: device = await ble_scan() if device is None: continue print(f"device is: {device}, name is {device.name()}") try: print(f"Connecting to {device.name()}") connection = await device.device.connect() except asyncio.TimeoutError: print("Timeout during connection") continue print(f"{IAM} connected to {connection}") # Discover services async with connection: try: service = await connection.service(BLE_SVC_UUID) characteristic = await service.characteristic(BLE_CHARACTERISTIC_UUID) except (asyncio.TimeoutError, AttributeError): print("Timed out discovering services/characteristics") continue except Exception as e: print(f"Error discovering services {e}") await connection.disconnect() continue tasks = [ asyncio.create_task(receive_data_task(characteristic)), ] await asyncio.gather(*tasks) await connection.disconnected() print(f"{BLE_NAME} disconnected from {device.name()}") break What’s Happening? Scanning: The central scans for peripherals advertising the specific service UUID. Connecting: Once it finds the peripheral, it connects and discovers the available services and characteristics. Communication: The central then listens for data from the peripheral and can send responses back. 3. Sending and Receiving Data Sending Data The central sends messages to the peripheral: async def send_data_task(connection, characteristic): global message_count while True: message = f"{MESSAGE} {message_count}" message_count += 1 print(f"sending {message}") try: msg = encode_message(message) characteristic.write(msg) await asyncio.sleep(0.5) response = decode_message(characteristic.read()) print(f"{IAM} sent: {message}, response {response}") except Exception as e: print(f"writing error {e}") continue await asyncio.sleep(0.5) Receiving Data The peripheral receives messages and can respond: async def receive_data_task(characteristic): global message_count while True: try: data = await characteristic.read() if data: print(f"{IAM} received: {decode_message(data)}, count: {message_count}") await characteristic.write(encode_message("Got it")) await asyncio.sleep(0.5) message_count += 1 except asyncio.TimeoutError: print("Timeout waiting for data in {ble_name}.") break except Exception as e: print(f"Error receiving data: {e}") break 4. Encoding and Decoding Messages BLE communication requires data to be sent in a specific format (bytes). We use two simple functions to encode and decode messages: def encode_message(message): return message.encode('utf-8') def decode_message(message): return message.decode('utf-8') Running the Code Peripheral Device: Set IAM = "Peripheral" and run the code on one Pico W. Central Device: Set IAM = "Central" and run the code on the other Pico W. The devices will establish a connection, and you’ll see messages being sent from the central to the peripheral and responses coming back. Conclusion In this tutorial, we explored how to use MicroPython and BLE to enable two Raspberry Pi Pico W devices to communicate wirelessly. We set up one device as a central and the other as a peripheral, enabling a two-way communication system. This foundation can be expanded into more complex IoT applications, allowing devices to interact in real-time over BLE. Whether you’re building a simple remote control or a complex sensor network, understanding how to leverage BLE in your projects opens up a world of possibilities. Happy coding!