bluetooth
BLE 扫描、连接、服务发现和特征读写。
BLE 中心模式:扫描外设、连接、发现服务、读写特征值。
边界:面向低功耗蓝牙中心端(扫描/连接其他设备),不是普通 HTTP/WebSocket 网络。若要让本机被扫描,请用 ble_peripheral。需要蓝牙硬件与系统授权;扫描、连接放在按钮回调,不要在 body() 里自动执行。
#模块概览
| 项 | 说明 |
|---|---|
| 导入 | import bluetooth |
| 适合做什么 | 传感器读取、Beacon 探测、自定义 BLE 外设通信 |
| 调用时机 | scan / connect / read / write 放在按钮回调 |
| 推荐顺序 | state() → scan() → connect() → discover_services() → 读写 → disconnect() |
| 数据格式 | write() 传 Base64 字符串;read() 返回 Base64 |
#快速开始
下面脚本检查蓝牙状态并扫描 5 秒:
已复制
import bluetooth
print("状态:", bluetooth.state())
if bluetooth.state() == "powered_on":
devices = bluetooth.scan(duration=5)
for d in devices:
print(d.get("name"), d.get("uuid"), d.get("rssi"))
else:
print("蓝牙不可用,请到设置中开启")
连接并发现服务:
已复制
import bluetooth
if bluetooth.state() == "powered_on":
devices = bluetooth.scan(duration=5.0)
if devices:
uuid = devices[0]["uuid"]
result = bluetooth.connect(uuid)
if result == "ok":
try:
services = bluetooth.discover_services(uuid)
print(f"发现 {len(services)} 个服务")
finally:
bluetooth.disconnect(uuid)
else:
print("连接失败:", result)
#AppUI 示例
扫描、连接分步放在按钮回调;失败时保留可读状态。
已复制
import appui
import bluetooth
state = appui.State(
ble_state="—",
device_count="0",
status="未扫描",
detail="点击扫描开始",
)
session = {"uuid": None}
def refresh_ble_state():
state.ble_state = str(bluetooth.state())
def scan_devices():
refresh_ble_state()
if state.ble_state != "powered_on":
state.batch_update(
status="蓝牙不可用",
detail=f"当前: {state.ble_state}",
device_count="0",
)
return
devices = bluetooth.scan(duration=5.0) or []
state.device_count = str(len(devices))
if not devices:
state.batch_update(
status="未发现设备",
detail="请靠近外设后重试",
)
return
target = devices[0]
session["uuid"] = target.get("uuid")
name = target.get("name") or "未命名设备"
state.batch_update(
status="已发现设备",
detail=f"{name} · RSSI {target.get('rssi', '—')}",
)
def connect_first():
uuid = session.get("uuid")
if not uuid:
state.detail = "请先扫描"
return
result = bluetooth.connect(uuid)
if result != "ok":
state.batch_update(status="连接失败", detail=str(result))
return
services = bluetooth.discover_services(uuid) or []
state.batch_update(
status="已连接",
detail=f"{len(services)} 个服务",
)
def disconnect_device():
uuid = session.get("uuid")
if uuid:
bluetooth.disconnect(uuid)
session["uuid"] = None
state.batch_update(status="已断开", detail="连接已释放")
def body():
return appui.NavigationStack(
appui.Form([
appui.Section("蓝牙", [
appui.LabeledContent("系统状态", value=state.ble_state),
appui.LabeledContent("扫描结果", value=state.device_count),
appui.Button("刷新状态", action=refresh_ble_state),
appui.Button("扫描设备", action=scan_devices)
.button_style("bordered_prominent"),
appui.Button("连接第一个", action=connect_first),
appui.Button("断开", action=disconnect_device, role="destructive"),
]),
appui.Section("详情", [
appui.LabeledContent("状态", value=state.status),
appui.Text(state.detail).foreground_color("secondaryLabel"),
], footer="真机测试;示例只连扫描到的第一台设备。"),
]).navigation_title("蓝牙")
)
appui.run(body, state=state)
#API 参考
#速查
| API | 作用 |
|---|---|
state() | BLE 状态字符串 |
scan(duration) | 扫描外设列表 |
stop_scan() | 立即停止扫描 |
connect(uuid) | 连接 → ok 或错误信息 |
disconnect(uuid) | 断开连接 |
discover_services(uuid) | 服务与特征列表 |
read(...) | 读特征 → Base64 字符串 |
write(...) | 写特征(Base64 入参)→ bool |
#状态与扫描
state() 常见返回值:
| 值 | 含义 |
|---|---|
powered_on | 蓝牙已开启,可扫描 |
powered_off | 蓝牙关闭 |
unauthorized | 未授权 |
unsupported | 设备不支持 BLE |
scan(duration=5.0) — 返回 [{uuid, name, rssi}, ...]。
已复制
devices = bluetooth.scan(duration=3.0)
bluetooth.stop_scan() # 提前结束
#连接与发现
connect(uuid) — 成功返回 "ok",失败返回错误描述字符串(不是异常)。
discover_services(uuid) — 返回服务字典列表(含特征 UUID),须先连接成功。
#读写特征
已复制
import base64
data_b64 = bluetooth.read(peripheral_uuid, service_uuid, char_uuid)
raw = base64.b64decode(data_b64) if data_b64 else b""
payload = base64.b64encode(b"hello").decode("ascii")
ok = bluetooth.write(peripheral_uuid, service_uuid, char_uuid, payload)
write() 的第四个参数必须是 Base64 字符串,不要直接传 bytes。
#推荐流程
state()确认powered_onscan()找到目标uuidconnect(uuid)discover_services(uuid)获取service_uuid/characteristic_uuidread()/write()disconnect(uuid)(建议try/finally)
#常见错误
| 错误写法 | 后果 | 修正 |
|---|---|---|
判断 poweredOn | 永远不匹配 | 使用 powered_on |
write() 直接传 bytes | 类型错误 | 先 base64.b64encode |
不检查 connect 返回值 | 未连接就读写 | 判断 == "ok" |
忘记 disconnect() | 连接残留 | finally 里断开 |
在 body() 里 scan() | 刷新时反复扫描耗电 | 放进按钮回调 |
| 与 ble_peripheral 混淆 | 调错 API | 广播用外设模块 |
#相关文档
| 文档 | 用途 |
|---|---|
| ble_peripheral | 本机作为 BLE 外设广播 |
| network | HTTP 网络请求 |
| websocket | WebSocket 连接 |
| permission | 蓝牙权限状态查询 |
| 原生能力入口 | MiniApp 场景配方 |
#预期效果
运行示例后,界面应出现文档描述的目标结果;若与预期不符,先看「失败路径」并按返回值或日志排查。