BLE-связь между Raspberry Pi и Raspberry Pi Pico W

В этом руководстве объясняется, как настроить связь по Bluetooth Low Energy (BLE) между Raspberry Pi и Pico W. Мы начнём с рассмотрения основ BLE, включая роли центрального (Central) и периферийного (Peripheral) устройств. После этого мы рассмотрим два простых примера, в которых Pico отправляет данные на Pi.

BLE-связь между Raspberry Pi и Raspberry Pi Pico W

Впервые работаете с Raspberry Pi Pico? Изучите Raspberry Pi Pico/Pico W с MicroPython с помощью нашей электронной книги

Это руководство было написано Edgardo Peregrino и отредактировано Sara Santos.

Предварительные требования

Вот список предварительных требований для этого руководства.

1) Плата Raspberry Pi

Вам нужна плата Raspberry Pi с Bluetooth. Можно использовать следующие модели: Pi 3, 3B+, 3A+, 4, 400, 5, 500, Zero W, Zero 2W, CM4 и CM5. При использовании модулей CM4 и CM5 убедитесь, что они включают модуль WiFi/Bluetooth.

Raspberry Pi 5

1.1) Raspberry Pi OS

С точки зрения программного обеспечения вы можете использовать как Raspberry Pi OS Desktop, так и Raspberry Pi OS Lite. Мы используем 64-битную версию.

1.2) Терминал и SSH

Вы также должны иметь некоторое представление о терминале, особенно поскольку вам потребуется включить службу Bluetooth через терминал и установить необходимые пакеты.

Мы будем использовать программу PuTTY для установки SSH-подключения к плате RPi и выполнения команд в окне терминала.

Мы рекомендуем следовать следующему руководству, если вы не знакомы с установкой Raspberry Pi OS или подключением через SSH с помощью PuTTY:

2) Плата Raspberry Pi Pico

Вам нужна плата Raspberry Pi Pico с Bluetooth. Вы можете использовать Raspberry Pi Pico W или Raspberry Pi Pico 2 W.

2.1) Thonny IDE

Для программирования Raspberry Pi Pico мы предпочитаем использовать Thonny IDE. Убедитесь, что на вашей плате установлена последняя версия прошивки MicroPython. Мы рекомендуем быстро ознакомиться со следующими руководствами, чтобы узнать, как использовать Thonny IDE, как прошить MicroPython и как запускать и загружать код на плату:

Основы BLE или Bluetooth Low Energy

Прежде чем начать, мы должны обсудить основы BLE или Bluetooth Low Energy. BLE — это протокол, который отличается от Bluetooth Classic.

Логотип Bluetooth Smart

Bluetooth Classic предназначен для непрерывной потоковой передачи данных, например, для воспроизведения музыки на беспроводной колонке. В отличие от него, Bluetooth Low Energy (BLE) оптимизирован для устройств и датчиков, что делает его подходящим для Pico W и моделей Raspberry Pi со встроенным Bluetooth.

BLE потребляет значительно меньше энергии, чем Bluetooth Classic, что делает его идеальным для микроконтроллеров и небольших устройств, хотя у него более короткий радиус действия. Ещё одно ключевое отличие заключается в способе подключения: Bluetooth Classic использует последовательную (Serial) связь, в то время как BLE опирается на протокол GATT (Generic Attribute Profile).

Для более полного введения в BLE и основных понятий, таких как периферийное устройство и контроллер, UUID, профили GATT и других, мы рекомендуем прочитать вводную часть этого руководства: :doc:`Raspberry Pi Pico W: Bluetooth Low Energy (BLE) с MicroPython </raspberry/pico/raspberry-pi-pico-w-bluetooth-low-energy-micropython/index>`_.

Обзор проекта

При использовании Bluetooth Low Energy (BLE) важно понимать роли BLE Peripheral и BLE Controller (также называемого Central Device — центральным устройством).

В данном случае (в примерах, рассматриваемых в этом руководстве) Raspberry Pi будет центральным устройством (Central), а Pico будет периферийным устройством (Peripheral).

Pico настраивает профиль GATT для обеспечения связи с Raspberry Pi. После подключения он «отправляет» сообщение на Pi, которое затем отображается в терминале. Такая конфигурация может быть полезна для проектов, например, использования Pico в качестве контроллера для робота на базе Raspberry Pi.

Примечание: Слово «отправляет» взято в кавычки, потому что Pico на самом деле не отправляет данные на Pi напрямую. Вместо этого Pico записывает значение в характеристику (characteristic) своего профиля GATT. Raspberry Pi, который подключён к Pico по BLE, затем считывает эту характеристику для получения данных.

Ниже приведён пример того, что мы будем делать в этом проекте.

Схема, показывающая как Pico в роли периферийного устройства связывается с Pi в роли центрального устройства BLE
  1. Периферийное устройство BLE (сервер) объявляет о своём присутствии (advertise).

  2. Центральное устройство BLE (клиент) сканирует BLE-устройства.

  3. Когда центральное устройство находит нужное периферийное устройство, оно подключается к нему.

  4. После подключения оно считывает профиль GATT периферийного устройства и ищет нужный сервис.

  5. Если сервис найден, оно может взаимодействовать с характеристиками. Например, считывать значения.

В BLE каждый сервис и характеристика должны иметь UUID (Universally Unique Identifier — универсально уникальный идентификатор). Эти UUID выступают в качестве уникальных адресов, которые Raspberry Pi использует для нахождения и подключения к сервисам Pico. Например, если Pico предоставляет сервис с характеристиками чтения и записи, каждая из них будет иметь свой UUID. Pi ищет эти UUID, чтобы знать, как взаимодействовать с ними.

Для более полного введения в BLE и основных понятий, таких как периферийное устройство и контроллер, UUID, профиль GATT и других, мы рекомендуем прочитать вводную часть этого руководства: :doc:`Raspberry Pi Pico W: Bluetooth Low Energy (BLE) с MicroPython </raspberry/pico/raspberry-pi-pico-w-bluetooth-low-energy-micropython/index>`_.

Необходимые компоненты

Вот список компонентов, необходимых для этого руководства:

Вы можете использовать приведённые выше ссылки или перейти непосредственно на MakerAdvisor.com/tools, чтобы найти все компоненты для ваших проектов по лучшей цене!

Подготовка Raspberry Pi

После начала работы со свежей установкой Raspberry Pi OS на вашей плате RPi, вам необходимо следовать следующим инструкциям, чтобы включить Bluetooth на Pi и установить некоторые необходимые пакеты.

Включение Bluetooth на Pi

Если вы используете версию Pi OS с рабочим столом, включить Bluetooth очень просто. Перейдите к значку Bluetooth и нажмите Make Discoverable. Вот и всё, вы готовы к работе.

Если вы используете Lite-версию или находитесь в SSH-сессии, мы будем использовать команды для включения Bluetooth. Установите SSH-подключение к вашему Pi.

Введите следующую команду:

sudo systemctl start bluetooth
Включение Bluetooth на Raspberry Pi в окне терминала

После этого выполните следующие команды по порядку:

sudo bluetoothctl
agent on
default-agent
Включение Bluetooth на RPi завершено

Эти команды необходимы для включения Bluetooth. Когда всё будет готово, вы можете выйти, набрав exit.

exit

Установка pigpio

Поскольку мы также будем управлять GPIO Pi, нам нужен пакет для их управления. Мы будем использовать pigpio. Выполните следующие команды для его глобальной установки и включения при загрузке.

sudo apt install pigpio
sudo systemctl enable pigpiod

Создание виртуального окружения

Мы создадим виртуальное окружение, которое использует общесистемные пакеты, такие как pigpio (я делаю это таким образом вместо установки внутри виртуального окружения, потому что обнаружил некоторые проблемы с управлением GPIO при использовании Raspberry Pi 5).

Во-первых, мы рекомендуем создать отдельную директорию, чтобы ваши файлы были организованы. Если вы используете версию Desktop, в идеале вам следует перейти в директорию Documents и создать там директорию. Например, вы можете назвать её bluetooth_samples.

Если вы используете Lite-версию, вы можете создать директорию bluetooth_samples и затем перейти в неё следующим образом:

mkdir bluetooth_samples && cd bluetooth_samples

Теперь вы должны находиться в папке bluetooth_samples.

Создание и переход в папку bluetooth_samples на Raspberry Pi

Теперь создайте виртуальное окружение с именем blue в этой директории.

python3 -m venv blue --system-site-packages

Активируйте виртуальное окружение следующим образом:

source blue/bin/activate

Вы поймёте, что оно активно, потому что имя виртуального окружения появится перед приглашением командной строки.

Виртуальное окружение Raspberry Pi активировано

Установка Bleak

Для Raspberry Pi нам нужно установить BLE-библиотеку, которая будет взаимодействовать с Pico, — она называется Bleak. Установите библиотеку в виртуальном окружении:

pip install bleak

Она должна установиться через несколько секунд.

Установка Bleak на Raspberry Pi - окно терминала

Схема подключения Raspberry Pi

В случае Raspberry Pi для нашего примера понадобится светодиод, подключённый к GPIO 17. Вы можете следовать следующей схеме подключения.

BLE-связь между RPi и RPi Pico

Здесь мы подключаем длинную ножку светодиода к GPIO-контакту 17 — это будет наш светодиод для проверки правильной работы кода. Короткая ножка светодиода подключается к резистору, а провод заземления — к отрицательной шине макетной платы, где расположен резистор.

Pico будет установлен на макетной плате просто из практических соображений, и на этом подключение завершено. Если хотите, вам не обязательно устанавливать Pico на макетную плату.

Отправка базового сообщения с Pico на Pi

В этом разделе мы покажем вам простой код MicroPython, который будет использоваться на Pico для «отправки» сообщений на Raspberry Pi.

Вы можете назвать его ble_sample.py. Позже вы можете загрузить его на Pico как main.py, чтобы он запускался без подключения к компьютеру.

Впервые работаете с BLE на Raspberry Pi Pico? Вы можете прочитать наше руководство для начинающих: :doc:`Raspberry Pi Pico W: Bluetooth Low Energy (BLE) с MicroPython </raspberry/pico/raspberry-pi-pico-w-bluetooth-low-energy-micropython/index>`_.

Скопируйте следующий код в Thonny IDE.

# Rui Santos & Sara Santos - Random Nerd Tutorials
# Complete project details at https://RandomNerdTutorials.com/ble-raspberry-pi-and-pi-pico-w/

import asyncio
import aioble
import bluetooth
from machine import Pin

# Bluetooth configuration
_SERVICE_UUID = bluetooth.UUID(0x1848)
_WRITE_CHARACTERISTIC_UUID = bluetooth.UUID(0x2A6E) # Central writes here
_READ_CHARACTERISTIC_UUID = bluetooth.UUID(0x2A6F)  # Peripheral writes message here

BLE_NAME = "Pico W Peripheral"

# Initialize LED
led = Pin("LED", Pin.OUT)

connected = False

# Register GATT server
ble_service = aioble.Service(_SERVICE_UUID)
read_characteristic = aioble.Characteristic(
    ble_service, _READ_CHARACTERISTIC_UUID, read=True, notify=True
)
aioble.register_services(ble_service)

# Helper to encode the message
def _encode_message(message):
    return message.encode('utf-8')

# Task to handle LED blinking and message sending
async def send_task():
    global connected
    message_count = 1
    while True:
        led.toggle()
        blink = 1000 if connected else 250
        if connected:
            message = f"Hello from {BLE_NAME}! Count: {message_count}"
            read_characteristic.write(_encode_message(message), send_update=True)
            message_count += 1
            print(f"Sent: {message}")
        await asyncio.sleep_ms(blink)

# Serially wait for connections
async def peripheral_task():
    global connected
    # Show MAC address once at start
    ble = bluetooth.BLE()
    _, mac_address = ble.config('mac')
    formatted_mac = ':'.join('{:02X}'.format(b) for b in mac_address)
    print(f"Bluetooth MAC Address: {formatted_mac}")

    while True:
        try:
            async with await aioble.advertise(
                2000, name=BLE_NAME, services=[_SERVICE_UUID], appearance=768
            ) as connection:
                connected = True
                print("Connection from", connection.device)
                await connection.disconnected()
        except Exception as e:
            print("Error in peripheral_task:", e)
        finally:
            connected = False
            print(f"{BLE_NAME} disconnected")
            await asyncio.sleep_ms(100)

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

# Run the program
asyncio.run(main())

Посмотреть исходный код

Как работает код?

Давайте разберём этот код для лучшего понимания.

Импорт библиотек

Сначала мы импортируем библиотеки, как показано ниже.

import asyncio
import aioble
import bluetooth
from machine import Pin

Определение UUID

Далее нам нужно определить UUID для Pico. Нам нужны UUID для сервиса, характеристик записи и чтения.

# Bluetooth configuration
_SERVICE_UUID = bluetooth.UUID(0x1848)
_WRITE_CHARACTERISTIC_UUID = bluetooth.UUID(0x2A6E) # Central writes here
_READ_CHARACTERISTIC_UUID = bluetooth.UUID(0x2A6F)  # Peripheral writes message here

Имя BLE

Мы определяем имя для нашего BLE-устройства Raspberry Pi Pico. Вы можете дать ему любое другое имя.

BLE_NAME = "Pico W Peripheral"

Инициализация светодиода

Следующая строка инициализирует встроенный светодиод RPi Pico как GPIO-выход с именем led.

# Initialize LED
led = Pin("LED", Pin.OUT)

Переменная connected

Переменная connected будет использоваться для отслеживания того, подключён ли Pico к другому BLE-устройству.

connected = False

Регистрация сервиса и характеристики

Затем регистрируем GATT-сервис и характеристику.

# Register GATT server
ble_service = aioble.Service(_SERVICE_UUID)
read_characteristic = aioble.Characteristic(
    ble_service, _READ_CHARACTERISTIC_UUID, read=True, notify=True
)
aioble.register_services(ble_service)

Кодирование BLE-сообщения

Сообщение, отправляемое по BLE, будет закодировано в UTF-8. Следующая функция encode_message() делает именно это.

# Helper to encode the message
def _encode_message(message):
    return message.encode('utf-8')

Запись в характеристику — функция send_task()

Функция send_task() «отправляет» сообщение на Raspberry Pi после подключения. Она также заставляет встроенный светодиод Raspberry Pi Pico мигать каждую секунду при подключении. Если не подключён, светодиод будет мигать быстрее — каждые 250 миллисекунд.

async def send_task():
    global connected
    message_count = 1
    while True:
        led.toggle()
        blink = 1000 if connected else 250
        if connected:
            message = f"Hello from {BLE_NAME}! Count: {message_count}"
            read_characteristic.write(_encode_message(message), send_update=True)
            message_count += 1
            print(f"Sent: {message}")
        await asyncio.sleep_ms(blink)

Новое сообщение отправляется каждую секунду. Мы добавляем счётчик к сообщению, чтобы отслеживать, сколько сообщений было отправлено.

message = f"Hello from {BLE_NAME}! Count: {message_count}"

Это строка, которая «отправляет» новое сообщение.

read_characteristic.write(_encode_message(message), send_update=True)

Счётчик message_count увеличивается в каждом цикле.

message_count += 1

Обратите внимание, что send_task — это асинхронная функция. Для работы с BLE лучше использовать асинхронное программирование, чтобы избежать проблем с таймингом. Если вы хотите узнать больше об асинхронном программировании с Raspberry Pi Pico на MicroPython, мы рекомендуем следующее руководство:

Объявление Pico как BLE-сервиса — peripheral_task()

Помимо записи в характеристику температуры, нам также нужно объявить Raspberry Pi Pico как BLE-сервис. Для этого мы используем функцию peripheral_task(), которая настраивает Pico как периферийное устройство и начинает объявление (advertising).

Мы также получаем и выводим MAC-адрес Pico — он понадобится нам позже на стороне Raspberry Pi для подключения к Pico.

# Serially wait for connections
async def peripheral_task():
    global connected
    # Show MAC address once at start
    ble = bluetooth.BLE()
    _, mac_address = ble.config('mac')
    formatted_mac = ':'.join('{:02X}'.format(b) for b in mac_address)
    print(f"Bluetooth MAC Address: {formatted_mac}")

    while True:
        try:
            async with await aioble.advertise(
                2000, name=BLE_NAME, services=[_SERVICE_UUID], appearance=768
            ) as connection:
                connected = True
                print("Connection from", connection.device)
                await connection.disconnected()
        except Exception as e:
            print("Error in peripheral_task:", e)
        finally:
            connected = False
            print(f"{BLE_NAME} disconnected")
            await asyncio.sleep_ms(100)

Главная функция

Наконец, мы создаём асинхронную функцию main(), в которой напишем основу нашего кода. Мы создаём две асинхронные задачи: одну для объявления и другую для отправки новых сообщений каждую секунду и мигания встроенного светодиода. Затем мы собираем и запускаем задачи асинхронно, вызывая функцию main().

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

# Run the program
asyncio.run(main())

Запуск кода

Запустите код на Pico, нажав зелёную кнопку воспроизведения в Thonny или нажав F5 на клавиатуре. После выполнения он отобразит MAC-адрес Pico.

Raspberry Pi Pico - отображение Bluetooth MAC-адреса

Raspberry Pi — Приём сообщений от RPi Pico

На Raspberry Pi создайте новый файл с кодом ниже. Вы можете назвать его pi_led_receive.py. Файл должен находиться внутри папки, которую мы создали ранее, — bluetooth_samples — в том же месте, где мы создали наше виртуальное окружение.

Существует множество способов создания и запуска файлов на Pi. Мне нравится использовать :doc:`Remote-SSH в VS Code </raspberry/rnt/raspberry-pi-remote-ssh-vs-code/index>`_. Это расширение позволяет установить SSH-подключение к вашему Pi, создавать файлы, писать код и выполнять его непосредственно на вашей плате Raspberry Pi с вашего компьютера через интерфейс VS Code. Узнайте больше здесь: :doc:`Программирование Raspberry Pi удалённо с помощью VS Code (Remote-SSH) </raspberry/rnt/raspberry-pi-remote-ssh-vs-code/index>`_.

# Rui Santos & Sara Santos - Random Nerd Tutorials
# Complete project details at https://RandomNerdTutorials.com/ble-raspberry-pi-and-pi-pico-w/

import asyncio
from bleak import BleakClient, uuids
from gpiozero import LED

connected = False

led = LED(17)

# Replace with the MAC address of your Pico
pico_address = "FF:FF:FF:FF:FF:FF"

# Service UUID (0x1848)
SERVICE_UUID = uuids.normalize_uuid_16(0x1848)
WRITE_CHARACTERISTIC_UUID = uuids.normalize_uuid_16(0x2A6E) # Central writes here
READ_CHARACTERISTIC_UUID = uuids.normalize_uuid_16(0x2A6F)  # Central reads here

async def receive_data_task(client):
    """Receive data from the peripheral device."""
    while True:
        try:
            response = await client.read_gatt_char(READ_CHARACTERISTIC_UUID)
            print(f"Central received: {response.decode('utf-8')}")
            await asyncio.sleep(1)
        except Exception as e:
            print(f"Error receiving data: {e}")
            break

async def blink_task():
    global connected
    print("blink task started")
    while True:
        led.toggle()
        blink = 1000  if connected else 250
        await asyncio.sleep(blink / 1000)

async def connect_and_communicate(address):
    global connected
    """Connect to the peripheral and manage data exchange."""
    print(f"Connecting to {address}...")

    async with BleakClient(address) as client:
        connected = client.is_connected
        print(f"Connected: {connected}")

        # Create tasks for sending and receiving data
        tasks = [
            asyncio.create_task(receive_data_task(client)),
            asyncio.create_task(blink_task())
        ]
        await asyncio.gather(*tasks)
    connected = False

# Run the connection and communication
loop = asyncio.get_event_loop()
loop.run_until_complete(connect_and_communicate(pico_address))

Посмотреть исходный код

Перед запуском кода вам нужно изменить следующую строку, указав Bluetooth MAC-адрес Pico. Вы должны были получить его в предыдущем примере.

# Replace with the MAC address of your Pico
pico_address = "2C:CF:67:B6:D7:4C"

Как работает код?

Давайте теперь быстро рассмотрим код, чтобы понять, как он работает.

Сначала импортируем необходимые библиотеки. Мы подключаем bleak для Bluetooth-функций.

import asyncio
from bleak import BleakClient, uuids
from gpiozero import LED

У нас есть булева переменная для отслеживания того, подключены ли мы к другому Bluetooth-периферийному устройству или нет.

connected = False

Мы определяем UUID сервиса и характеристик. Это сервис и характеристики Pico, которые мы будем искать для чтения сообщения, отправленного с Pico. Поэтому они должны совпадать с UUID на Pico.

# Service UUID (0x1848)
SERVICE_UUID = uuids.normalize_uuid_16(0x1848)
WRITE_CHARACTERISTIC_UUID = uuids.normalize_uuid_16(0x2A6E) # Central writes here
READ_CHARACTERISTIC_UUID = uuids.normalize_uuid_16(0x2A6F)  # Central reads here

Затем у нас есть асинхронная функция receive_data_task(), которая ожидает чтения значения характеристики периферийного устройства. Она читает характеристику каждую секунду.

async def receive_data_task(client):
    """Receive data from the peripheral device."""
    while True:
        try:
            response = await client.read_gatt_char(READ_CHARACTERISTIC_UUID)
            print(f"Central received: {response.decode('utf-8')}")
            await asyncio.sleep(1)
        except Exception as e:
            print(f"Error receiving data: {e}")
            break

У нас есть ещё одна задача — blink_task(), которая будет выполняться одновременно. Эта задача будет мигать светодиодом, подключённым к GPIO 17 на Raspberry Pi. Она будет выполняться каждую секунду, если мы подключены к периферийному устройству.

async def blink_task():
    global connected
    print("blink task started")
    while True:
        led.toggle()
        blink = 1000  if connected else 250
        await asyncio.sleep(blink / 1000)

Наконец, функция connect_and_communicate() пытается подключиться к периферийному устройству с MAC-адресом, который мы определили ранее. После подключения мы создаём и собираем задачи для приёма данных и мигания светодиода.

async def connect_and_communicate(address):
    global connected
    """Connect to the peripheral and manage data exchange."""
    print(f"Connecting to {address}...")

    async with BleakClient(address) as client:
        connected = client.is_connected
        print(f"Connected: {connected}")

        # Create tasks for sending and receiving data
        tasks = [
            asyncio.create_task(receive_data_task(client)),
            asyncio.create_task(blink_task())
        ]
        await asyncio.gather(*tasks)
    connected = False

Наконец, мы асинхронно запускаем задачи в цикле.

# Run the connection and communication
loop = asyncio.get_event_loop()
loop.run_until_complete(connect_and_communicate(pico_address))

Запуск Python-кода

После создания файла pi_led_receive.py с кодом, которым мы поделились ранее, и указания MAC-адреса вашего Pico, вы можете запустить его на вашей плате Raspberry Pi. Мы используем :doc:`расширение Remote-SSH в VS Code </raspberry/rnt/raspberry-pi-remote-ssh-vs-code/index>`_, но вы можете просто создать новый файл с помощью команды nano.

После сохранения файла вы можете запустить его. Но сначала убедитесь, что вы находитесь в виртуальном окружении, которое мы создали ранее. Если вы вышли из виртуального окружения, вы можете активировать его снова следующим образом:

source blue/bin/activate

Наконец, вы можете запустить вашу Python-программу следующим образом:

python3 pi_led_receive.py

Демонстрация

Код начнёт выполняться, и через несколько секунд подключится к Raspberry Pi Pico (убедитесь, что RPi Pico выполняет код в Thonny IDE).

Он начнёт получать сообщения от Pico.

Raspberry Pi получает BLE-сообщения от RPi Pico - окно терминала

В то же время светодиод, подключённый к GPIO 17, начнёт мигать каждую секунду.

BLE-связь между RPi и RPi Pico

Вот и всё. Теперь Raspberry Pi считывает данные с Raspberry Pi Pico по BLE.

Вы также можете проверить отправленные сообщения в консоли Thonny IDE.

RPi Pico отправляет Bluetooth-сообщение на Raspberry Pi

Отправка текущего времени с Pico на Pi

Вместо отправки бессмысленного сообщения мы можем легко изменить код для отправки показаний датчиков или команд, например. В этом разделе мы покажем вам, как можно легко изменить предыдущий пример для отправки других данных. В данном случае мы будем отправлять текущее время с Pico на Pi.

Код для Pi останется прежним. Для Pico нам нужно внести небольшие изменения. Вот код для Pico.

# Rui Santos & Sara Santos - Random Nerd Tutorials
# Complete project details at https://RandomNerdTutorials.com/ble-raspberry-pi-and-pi-pico-w/

import asyncio
import aioble
import bluetooth
from machine import Pin
import time

# Bluetooth configuration
_SERVICE_UUID = bluetooth.UUID(0x1848)
_WRITE_CHARACTERISTIC_UUID = bluetooth.UUID(0x2A6E) # Central writes here
_READ_CHARACTERISTIC_UUID = bluetooth.UUID(0x2A6F)  # Peripheral writes message here

BLE_NAME = "Pico W Peripheral"

# Initialize LED
led = Pin("LED", Pin.OUT)

connected = False

# Register GATT server
ble_service = aioble.Service(_SERVICE_UUID)
read_characteristic = aioble.Characteristic(
    ble_service, _READ_CHARACTERISTIC_UUID, read=True, notify=True
)
aioble.register_services(ble_service)

# Helper to encode the message
def _encode_message(message):
    return message.encode('utf-8')

# Task to handle LED blinking and message sending
async def send_task():
    global connected
    while True:
        led.toggle()
        blink = 1000 if connected else 250
        if connected:
            today = time.localtime()  # get the current time
            message = f"{today}"
            read_characteristic.write(_encode_message(message), send_update=True)
            print(f"Sent: {message}")
        await asyncio.sleep_ms(blink)

# Serially wait for connections
async def peripheral_task():
    global connected
    # Show MAC address once at start
    ble = bluetooth.BLE()
    _, mac_address = ble.config('mac')
    formatted_mac = ':'.join('{:02X}'.format(b) for b in mac_address)
    print(f"Bluetooth MAC Address: {formatted_mac}")

    while True:
        try:
            async with await aioble.advertise(
                2000, name=BLE_NAME, services=[_SERVICE_UUID], appearance=768
            ) as connection:
                connected = True
                print("Connection from", connection.device)
                await connection.disconnected()
        except Exception as e:
            print("Error in peripheral_task:", e)
        finally:
            connected = False
            print(f"{BLE_NAME} disconnected")
            await asyncio.sleep_ms(100)

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

# Run the program
asyncio.run(main())

Посмотреть исходный код

Сначала нам нужно добавить библиотеку для работы со временем.

import time

Затем нам нужно изменить функцию send_task(). По сути, после подключения мы получаем текущее время и сохраняем его в переменной today.

today = time.localtime()

Текущее время будет нашим сообщением.

message = f"{today}"

Затем мы просто записываем текущее время в характеристику следующим образом:

read_characteristic.write(encode_message(message), send_update=True)

Теперь легко понять, что вы можете легко изменить код для отправки любых других полезных данных, таких как показания датчиков или команды для управления выходами, например.

Демонстрация

На стороне Raspberry Pi соединение должно было разорваться с Pico, когда вы запустили этот новый код. Вам нужно снова запустить Python-код на Raspberry Pi сразу после запуска кода на Pico.

Через некоторое время устройства подключатся и начнут отправку сообщений.

На стороне Raspberry Pi он будет получать время, отправленное с Pico. Временная метка должна отображаться в окне терминала.

RPi получает время от Pico через BLE

Заключение

В этом руководстве мы изучили основы BLE и узнали, как настроить Pi для взаимодействия с Pico.

Мы планируем создать больше руководств на эту тему. В частности, рассмотреть двунаправленную связь, чтобы оба устройства могли отправлять и получать данные одновременно.

А пока вот несколько задач, если вы хотите развить этот пример дальше:

Надеемся, вы нашли это руководство полезным.

Особая благодарность нашему читателю Edgardo Peregrino, который создал и написал макет этого руководства, а также примеры кода.

Edgardo Peregrino — автор книг «Programming Raspberry Pi in 30 Days» и «Cloud Powered Robotics with Raspberry Pi», а также автор нескольких статей, особенно в журнале Servo Magazine. Вы можете ознакомиться с его работами на его странице GitHub или YouTube-канале (LinuxRobotGeek).

Узнайте больше о Raspberry Pi Pico и Raspberry Pi:

Спасибо за чтение.