关于Raspberry Pi Pico W的蓝牙的一切

PiCockpit Pico W 蓝牙标题图片

树莓派基金会最近宣布 发布C/C++ SDK 1.5.1版,支持蓝牙功能 Pico W上的C/C++ SDK和Micropython的新版本。

你猜怎么着?如果您有Pico W,您不需要购买新的。

如果您还没有 Pico W,可以购买一个 这里.

我们已经有一篇文章涉及 关于Pico W的所有信息.此外,如果您是 Pico W 的初学者,您可以 查看本文.

但在这里,我们将为您介绍您需要了解的一切,包括 蓝牙 在Raspberry Pi Pico W上。

皮克W背景

让我们先来了解一下 Pico W 的一些规格。

由于采用了CYW43439无线芯片,Pico W具有无线功能。

每个Pico W的核心是RP2040,它是Raspberry Pi的第一个硅芯片。

英飞凌CYW43439支持IEEE 802.11 b/g/n无线局域网(Wi-Fi)和蓝牙5.2。在发布时,固件和软件仅支持Wi-Fi。

CYW43439支持BLE,Wi-Fi和蓝牙共用一根天线。

树莓派和 Pico W 天线

如果你仔细看看Pico W,你会注意到类似于Raspberry Pi 4的三角形PCB天线。Raspberry Pi使用的是ABRACON公司授权的板载天线。

这意味着不需要额外的天线。无线接口通过SPI与RP2040连接。

ǞǞǞ 官方数据表 还建议,为获得最佳无线性能,不应在天线下方或靠近天线的地方放置任何金属。但是,在天线两侧添加接地金属可以提高天线的带宽。

板载的LED是通过英飞凌43439芯片的WL_GPIO0针脚控制的。在Pico上,LED被连接到GPIO针脚25。

此外,SWD调试引脚被移到了板子的中心位置,以便为PCB天线创造空间。你可以在RP2040和CYW43439之间找到它们,从左到右的顺序仍然是SWCLK、GND、SWDIO。

使用Pico W,您可以用C/C++和MicroPython编程。

经典蓝牙和BLE

Pico W可与蓝牙经典版和蓝牙低功耗版配合使用。蓝牙经典和蓝牙低功耗(BLE)是蓝牙规范中设备通信的两种不同方式。

蓝牙经典,也称为蓝牙基本速率/增强数据速率(BR/EDR),是蓝牙的原始版本。它专为高速数据传输、音频流和设备配对而设计。蓝牙经典版通常用于无线音频扬声器、键盘、鼠标和设备间文件传输等应用。

蓝牙低功耗(BLE)也称为蓝牙智能(Bluetooth Smart),是蓝牙的一种高能效变体。BLE是作为蓝牙4.0规范的一部分推出的,针对需要较长电池寿命的低功耗设备进行了优化,例如健身追踪器、智能手表、家庭自动化设备和无线传感器。

Pico W既可作为中心设备,也可作为外围设备。

在Pico W上开始使用蓝牙

让我们从一个快速项目开始,确保蓝牙正常工作。

我们将运行一个经典程序,让 Pico W 上的板载 LED 灯打开,但要使用蓝牙。

为此,您需要一个 Pico W、一根微型 USB 电缆和一块面包板。

Pico W

第一步

您首先需要下载 Thonny IDE如果你还没有的话。

Thonny是一种在Pico W上对MicroPython进行编程的超级简单的方法。

您还需要下载 此 UF2 文件它同时支持 Wi-Fi 和 BLE。下载完成后,按住 Pico W 上的 BOOTSEL 按钮,通过 USB 将其连接到电脑。约 3 秒后松开 BOOTSEL 按钮。

此时会出现一个名为 "RPI-RP2 "的远程驱动器。将 UF2 文件移至该驱动器。

然后驱动器将消失。

第二步

现在您可以断开并重新连接 Pico W(这次不要按住 BOOTSEL 按钮)。

打开Thonny。

Pico W 蓝牙 Thonny IDE 的图像
更换主题和字体,保护你的眼睛!

打开 Thonny 后,进入 "工具">"选项">"解释器",确保解释器设置为 Micropython,端口为 Pico W:

您的Pico W可能以不同的名称出现。

完成后,点击 "确定",就可以开始了。

现在,复制以下代码并粘贴到部分:

from micropython import const
import struct
import bluetooth

_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)

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)

    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[0], "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)[0]))
    for u in decode_field(payload, _ADV_TYPE_UUID32_COMPLETE):
        services.append(bluetooth.UUID(struct.unpack("<d", u)[0]))
    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()

将该文件保存到 Raspberry Pi Pico W 上,文件名为 "ble_advertising.py"。该文件的目的是让 Pico W 能够读取其他设备,也能被其他设备读取。

接下来,在Thonny中打开一个新文件并粘贴以下代码:

# PiCockpit.com

import bluetooth
import random
import struct
import time
from machine import Pin
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):
        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)
            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():
    led_onboard = Pin("LED", Pin.OUT)
    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():
            led_onboard.on()
            for _ in range(3):
                data = str(i) + "_"
                print("TX", data)
                p.send(data)
                i += 1
        time.sleep_ms(100)


if __name__ == "__main__":
    demo()

现在,像以前一样,将文件保存到 Pico W 上,这次可以将其命名为 "led_peripheral.py"。广告文件能让 Pico W 与其他设备通信,而外设文件则能让它 功能 作为外围设备。在这种情况下,该功能将打开LED。

现在点击 Thonny 工具栏上的 "运行当前脚本"。

Pico W 蓝牙 Thonny IDE 的图像

如图所示,控制台将输出 "Starting advertising(开始广告)"。此时,您可以连接手机等蓝牙设备。查找并配对 "mpy-uart"。

安卓手机蓝牙连接图像

一旦配对成功,Pico W 的 LED 灯就会亮起,Thonny 控制台就会开始计算两者的配对时间:

Pico W 蓝牙 Thonny IDE 的图像

恭喜您!您现在已经确认 Pico W 的蓝牙功能正常,可以开始专注于下一个项目了!

Pico W 蓝牙项目

在开始下一个 Pico W 项目时,您可以参考其他人已经完成的一些项目。由于Pico W刚刚获得官方蓝牙支持,新项目才刚刚开始涌现。

以下是迄今为止已经出现的一些项目的清单(还在不断增加):

  1. Pico W 蓝牙 遥控器
  2. Pico W 蓝牙 机器人
  3. Pico W 蓝牙 鼠标
  4. Pico W 蓝牙 灯具
  5. Pico W USB转蓝牙 音频适配器
  6. Pico W 蓝牙 PicoDRO(数字读数)

常见问题

Pico W 可以使用蓝牙经典版吗?

当然,Pico W 支持蓝牙经典和 BLE!

Pico W 可以同时连接多台设备吗?

是的,Pico W 可以作为中心设备,同时连接多个蓝牙外围设备。您还可以同时使用蓝牙经典和 BLE。因此,您可以创建超复杂系统,让 Pico W 同时与多个传感器通信或控制多个设备。

Pico W 的蓝牙范围有多大?

在开阔空间通常可达 30 米(98 英尺)。不过,范围可能会因障碍物、干扰和信号强度等环境因素而异。

能否将 Pico W 用作蓝牙外围设备?

Pico W 可作为中心设备或外围设备使用。因此,它允许其他蓝牙中央设备(如智能手机、平板电脑或电脑)与之连接。所有这些都能与 码头顺便说一句

如何在 Pico W 上设置蓝牙功能?

您可以使用 Raspberry Pi Pico SDK 1.5.1 版对 Pico W 上的蓝牙功能进行编程。SDK 提供了专门用于蓝牙开发的库和示例。通过它,您可以实现蓝牙服务、特性并处理通信协议。

Pico W 蓝牙的电源要求是什么?

蓝牙的设计非常省电。因此,Pico W 在闲置期间的功耗极低,您可以通过使用睡眠模式和低功耗状态等技术进一步优化代码。具体的功耗要求显然取决于应用和使用场景。

11评论

  1. Marcos Gutierrez 在8 月 4, 2023在1:13 上午

    您好,我可以将微微镜改装成扫描振镜,请问您的答复是什么?

  2. Electronikmx 在8 月 17, 2023在12:16 上午

    经过几秒钟后,设备显示错误,并在需要连接时停止运行。

    • Adam 在8 月 17, 2023在6:51 上午

      在什么时候?

      • Electronikmx 在8 月 17, 2023在4:19 下午

        正如所评论的那样,在蓝牙连接请求中输入 "vincular "后,RPI pico W 会在 "TX 1_、TX 2_..." 标签中发送一系列程序自带的数字。但是,过了几秒钟之后,手机或平板电脑(我打算用 3 台设备)显示我在连接时出错了,手机蓝牙出现了 "连接失败 64 "的提示,而在下方新出现了 "开始广告 "的提示,我必须重新连接。

        • Javier 在10 月 5, 2023在11:08 下午

          我也是

  3. Alex 在10 月 2, 2023在8:00 上午

    在 pi pico W 上能否同时打开 Wifi 连接和蓝牙连接?如果不能,能否在两者之间实时切换?

  4. Javier 在10 月 5, 2023在11:10 下午

    Me pasa lo mismo.... ya lo intenté de varios dispositivos y aplicaciones.无法运行。

  5. Peter 在11 月 12, 2023在11:33 上午

    如何设置 kitronik mini Pico 控制器与 kitronik Pico 机器人板配对并进行控制?

  6. Brian 在11 月 27, 2023在12:27 上午

    你好,我按照你的说明进行了操作,但在我的 iPhone 蓝牙下找不到 mpy-uart。

    我的 Pico W 通过 USB 与电脑连接。
    我闪过了你的 UF2
    我复制了您的代码,并通过 Thonny 将文件保存到了 pico W 上的名称中
    我正在运行 Thonny,右下角是名为 MicroPython (Raspberry Pi Pico) 的工厂{dot. like there is hovering dot} Board CDC @ COM3
    我运行了 led_peripheral.py
    外壳输出 "开始广告"。
    在 iPhone 上看不到

    有什么想法?

    感谢您的贡献。

    • Anant 在3 月 19, 2024在2:31 下午

      嘿,我也遇到了同样的问题。你能解决这个问题吗?

  7. Paul B. 在2 月 22, 2024在3:04 下午

    我有一个 Arduino GPS 模块,它通过蓝牙传输坐标,而不是与手机进行 BT 连接,有办法用 Pico W 配对并读取该信号吗?

发表评论