Rockchip BLE 低功耗唤醒方案:基于广播扫描的产品级设计与实战(含可执行步骤与代码)

如何让小智AI成为你的第二大脑——“免费”送小智AI智能音箱征文活动 10w+人浏览 427人参与

AI助手已提取文章相关产品:

📺 B站视频讲解(Bilibili)https://www.bilibili.com/video/BV1k1C9BYEAB/

📘 《Yocto项目实战教程》京东购买链接Yocto项目实战教程


Rockchip BLE 低功耗唤醒方案:基于广播扫描的产品级设计与实战(含可执行步骤与代码)

目标:在 Rockchip 设备上,让 Camera 进入低功耗(系统 suspend),由 平板通过 BLE 广播唤醒,Camera 不建立连接,仅通过 Scan 接收广播 → 命中规则 → 触发系统唤醒 完成唤醒;唤醒后可选择 广播 30 秒在线状态

本文输出:

  • 核心原理:为什么“广播 + 扫描”适合低功耗唤醒;为什么不需要连接;RSSI 判定是谁做。
  • 协议设计:如何在广播包里“可产品化地识别目标设备”,避免依赖 MAC/名称。
  • Rockchip 实战:BlueZ/内核/电源管理的可执行步骤;如何抓包看到 advertise 内容;如何写代码解析;如何做“4 Camera + 1 平板”的唤醒。
  • 可执行方案:给出可运行的脚本与(可编译)C 程序,附 systemd 服务示例。

在这里插入图片描述

1. 先把模型说清楚:你要的不是“蓝牙连接”,而是“蓝牙事件触发唤醒”

1.1 广播/扫描的本质

  • Advertise(广播):设备向空气中“喊话”,单向发送。
  • Scan(扫描):设备“竖起耳朵听”,接收广播。
  • RSSI(信号强弱):永远由 接收方(Scan 端) 测量得出。

结论:

  • 想“唤醒自己”:你的设备必须具备 Scan 接收能力
  • 广播端永远不知道有没有人靠近,它只是在发。

1.2 为什么低功耗唤醒不推荐“连接”

连接(GATT)意味着:

  • 需要建立/维护连接状态(心跳、超时、参数协商)。
  • 功耗更高、稳定性更复杂。

而唤醒只是一个“事件触发”,最适合:

  • 广播发命令(平板)
  • 低占空比扫描(Camera)
  • 命中后触发唤醒(GPIO 或 wakeup source)

2. 产品级关键:不要用设备名称/MAC 做识别,使用 Manufacturer Specific Data(0xFF)

2.1 为什么不建议用 Name / MAC

  • 名称(Local Name):可变、可缺失、易仿冒,适合调试,不适合产品协议。
  • MAC 地址:BLE 支持随机地址(random address),很多设备会周期性变化,不适合长期绑定。

2.2 推荐:Manufacturer Specific Data(AD Type 0xFF)

BLE 广播数据是 TLV 结构:

[Len][Type][Value...] [Len][Type][Value...] ...

其中 Type=0xFF 表示 厂商自定义数据,行业主流 Beacon/IoT/门禁/车钥匙/Camera 唤醒都用它。

2.3 推荐协议(可直接量产)

定义一段固定长度的 Manufacturer Data(示例 7 字节负载):

字节偏移字段示例说明
0-1Company ID0x1234Bluetooth SIG 分配(示例值)
2Product ID0x01Camera 产品类型
3Target ID0xFF 或 0x01~0x040xFF=全部 Camera,1~4=指定 Camera
4CMD0xA50xA5=WAKE
5Token0x66简单校验/防误触(可扩展)
6CRC/Version0x01可选,版本/CRC

设计要点:固定长度、可快速匹配、便于 Controller 层过滤。


3. 系统级设计:4 台 Camera + 1 台平板的整体状态机

3.1 逻辑图(建议写进设计文档/PPT)

flowchart LR
  T[Tablet 平板
BLE Advertise: WAKE] -->|广播| AIR((Air))
  AIR --> A[Camera A
低功耗 Scan]
  AIR --> B[Camera B
低功耗 Scan]
  AIR --> C[Camera C
低功耗 Scan]
  AIR --> D[Camera D
低功耗 Scan]

  A -->|命中 Target=All 或 A_ID| WAKE_A[触发唤醒]
  B -->|命中 Target=All 或 B_ID| WAKE_B[触发唤醒]
  C -->|命中 Target=All 或 C_ID| WAKE_C[触发唤醒]
  D -->|命中 Target=All 或 D_ID| WAKE_D[触发唤醒]

  WAKE_A --> RUN_A[系统 Resume]
  RUN_A --> ADV_A[广播在线状态 30s]

3.2 Camera 端的最小判定逻辑

if company_id == 0x1234
and product_id == CAMERA
and cmd == WAKE
and (target == ALL || target == MY_ID)
and rssi > RSSI_THRESHOLD
then wake_system()
  • RSSI 阈值(例如 -65 dBm)用于避免远距离误唤醒。
  • Token 用于简单鉴权/防误触(可做滚动码升级)。

4. Rockchip 上实现低功耗唤醒:两种工程路线

路线 A(推荐,产品级):Controller 层过滤 + GPIO/唤醒源

  • BLE Controller 在低功耗仍保持低占空比扫描。
  • 命中特定 Manufacturer Data 后,触发 Wake GPIO / Wake IRQ
  • SoC 从 suspend/resume 起来。

优点:

  • 功耗最低
  • CPU 无需常驻解析
  • 抗干扰/稳定

路线 B(易落地,开发阶段):Host 解析(BlueZ)+ wakeup source

  • 系统未进入最深睡眠或使用 s2ram
  • HCI 事件上报到主机
  • 用户态/内核解析命中后触发唤醒

优点:

  • 更通用
  • 便于调试

本文优先讲:路线 B 可快速验证,再给出如何演进到路线 A。


5. 如何“看到”对方的 Advertise 数据:抓包与验证步骤(Rockchip)

5.1 环境检查

  1. 确认控制器存在:
hciconfig -a
  1. 确认 BlueZ 工具:
bluetoothctl -v
btmgmt --version || true
btmon -v || true

如果缺少工具:在 Yocto 中把 bluez5bluez5-tools(发行版命名可能略有差异)加入镜像。

5.2 使用 btmon 抓 HCI(推荐)

btmon 能看到 LE Advertising Report,包括 RSSI 与原始 payload。

btmon &
# 另一个终端开启扫描
bluetoothctl
[bluetooth]# power on
[bluetooth]# scan on

此时你会在 btmon 输出中看到类似:

  • 地址
  • RSSI
  • Advertising Data
  • Manufacturer Specific Data(0xFF)

5.3 使用 bluetoothctl 查看扫描结果(简版)

bluetoothctl
scan on
# 看到设备出现,但 bluetoothctl 默认不展示完整 payload

因此:

  • 要看 payload:用 btmon
  • 仅看设备出现:bluetoothctl 足够

6. 平板侧如何发“唤醒广播”:最小可执行方案

平板(Android/Linux)可以选择:

  • Android APP(BLE Advertiser API)
  • Linux 平板:bluetoothd + btmgmt 或 直接 HCI

本文给出 Linux 可执行路径(易验证)。

6.1 Linux 平板:用 btmgmt 发送广播(思路)

不同 BlueZ 版本在命令细节上存在差异,工程上更稳定的做法是:

  • 写一个小工具,直接通过 HCI 下发 LE Set Extended Advertising Data

下面给出 可运行的 HCI 脚本(与 Rockchip/BCM 常见脚本风格一致),用于发送 manufacturer data。

发送 WAKE 广播脚本(tablet_adv_wake.sh)
#!/bin/bash
set -e

# 示例:Company=0x1234, Product=0x01, Target=0xFF(All), CMD=0xA5, Token=0x66
COMPANY_L=0x34
COMPANY_H=0x12
PRODUCT=0x01
TARGET=${1:-0xFF}
CMD=0xA5
TOKEN=0x66

# Extended Advertising 参数/数据命令可能因控制器略有不同。
# 这里用 hcitool cmd 演示(部分系统已弃用 hcitool,但仍常用于工程验证)。

# 1) 设置扩展广播参数
hcitool cmd 0x08 0x0036 \
  0x00 0x13 0x00 0x90 0x01 0x00 0xc2 0x01 0x00 0x07 0x00 \
  0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x00 0x01 0x00 0x00

# 2) 设置扩展广播数据(TLV)
# Flags: 02 01 06
# Mfg:   07 FF 34 12 01 FF A5 66
hcitool cmd 0x08 0x0037 \
  0x00 0x03 0x01 \
  0x02 0x01 0x06 \
  0x07 0xFF $COMPANY_L $COMPANY_H $PRODUCT $TARGET $CMD $TOKEN

# 3) 启用广播
hcitool cmd 0x08 0x0039 0x01 0x01 0x00 0x00 0x00 0x00

echo "[tablet_adv_wake] advertising wake cmd target=${TARGET}"

使用方法:

chmod +x tablet_adv_wake.sh
# 唤醒全部 Camera
./tablet_adv_wake.sh 0xFF
# 唤醒 Camera #2
./tablet_adv_wake.sh 0x02

提示:上述扩展广告命令与参数在不同控制器上可能需要微调。验证阶段先用 btmon 观察是否成功发出 Manufacturer Data。


7. Camera 侧如何“Scan 并获取 advertise 内容”:两种写法

  • 写法 1:btmon 抓包 + 解析(调试用)
  • 写法 2:BlueZ D-Bus 扫描回调 + 解析 Manufacturer Data(产品软件层常用)

本文给出“写法 2”:用 C 通过 D-Bus 监听 org.bluez.Device1 的 ManufacturerData。


8. Camera 端 C 程序:扫描并解析 ManufacturerData,命中后执行唤醒动作

说明:为了“可执行”,这里给出一个 基于 BlueZ D-Bus 的实现框架。它适合:

  • 系统处于轻睡眠/待机(CPU 未完全断电或允许 D-Bus 服务运行)
  • 或作为开发阶段验证(先把协议与过滤跑通)

当你需要进入更深的低功耗,建议迁移到 Controller 层过滤 + GPIO 唤醒(见第 11 节)。

8.1 依赖

  • libdbus-1-dev(或对应 Yocto SDK 头文件)
  • BlueZ bluetoothd

8.2 代码:ble_wake_scanner.c

功能:

  • 打开 Adapter 扫描
  • 监听新增 Device 对象
  • 读取 ManufacturerDataRSSI
  • 命中协议后执行动作(示例:写一个文件/调用脚本)
// ble_wake_scanner.c
// build: gcc -O2 -Wall ble_wake_scanner.c -o ble_wake_scanner $(pkg-config --cflags --libs dbus-1)

#include <dbus/dbus.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define COMPANY_ID 0x1234
#define PRODUCT_ID 0x01
#define CMD_WAKE   0xA5

// 根据你的场景调整
#define RSSI_THRESHOLD (-65)
#define MY_CAMERA_ID   0x02   // 例:本机为 Camera #2

static void die(const char *msg) {
    fprintf(stderr, "%s\n", msg);
    exit(1);
}

// 触发唤醒动作:这里用最小可执行方式演示
// 真正产品可以:拉 GPIO、写 /sys/power/wakeup、发 DBus 给 power manager 等
static void trigger_wakeup(void) {
    fprintf(stdout, "[ble_wake] TRIGGER WAKEUP!\n");
    // 示例:调用外部脚本(你可以替换成 systemctl start xxx 或 GPIO 操作)
    system("/usr/local/bin/wake_action.sh");
}

// 解析 ManufacturerData(BlueZ 中是 dict<uint16, variant(array<byte>)>)
// 我们只关心 key==COMPANY_ID 的条目,并解析 value。
static int parse_mfg_data(DBusMessageIter *dict_iter, int rssi) {
    // dict entry: key(uint16) -> variant(array<byte>)
    DBusMessageIter entry;
    dbus_message_iter_recurse(dict_iter, &entry);

    if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_UINT16)
        return 0;

    dbus_uint16_t key = 0;
    dbus_message_iter_get_basic(&entry, &key);

    // move to value (variant)
    dbus_message_iter_next(&entry);
    if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
        return 0;

    DBusMessageIter var;
    dbus_message_iter_recurse(&entry, &var);

    if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_ARRAY)
        return 0;

    if (key != COMPANY_ID)
        return 0;

    // array of bytes
    DBusMessageIter arr;
    dbus_message_iter_recurse(&var, &arr);

    unsigned char buf[32];
    int n = 0;
    while (dbus_message_iter_get_arg_type(&arr) == DBUS_TYPE_BYTE && n < (int)sizeof(buf)) {
        dbus_message_iter_get_basic(&arr, &buf[n]);
        n++;
        dbus_message_iter_next(&arr);
    }

    // 期望协议:Product(1) Target(1) CMD(1) Token(1) ...
    if (n < 4)
        return 0;

    unsigned char product = buf[0];
    unsigned char target  = buf[1];
    unsigned char cmd     = buf[2];
    unsigned char token   = buf[3];

    if (product != PRODUCT_ID)
        return 0;

    if (!(target == 0xFF || target == MY_CAMERA_ID))
        return 0;

    if (cmd != CMD_WAKE)
        return 0;

    // Token 可扩展为 rolling code/CRC
    (void)token;

    if (rssi < RSSI_THRESHOLD) {
        fprintf(stdout, "[ble_wake] matched but RSSI too low: %d\n", rssi);
        return 0;
    }

    fprintf(stdout, "[ble_wake] matched WAKE (rssi=%d)\n", rssi);
    return 1;
}

// 监听 PropertiesChanged 信号,读取 ManufacturerData/RSSI
static void process_properties_changed(DBusMessage *msg) {
    DBusMessageIter it;
    if (!dbus_message_iter_init(msg, &it))
        return;

    // interface name
    const char *iface = NULL;
    if (dbus_message_iter_get_arg_type(&it) != DBUS_TYPE_STRING)
        return;
    dbus_message_iter_get_basic(&it, &iface);

    if (!iface || strcmp(iface, "org.bluez.Device1") != 0)
        return;

    // changed properties dict
    dbus_message_iter_next(&it);
    if (dbus_message_iter_get_arg_type(&it) != DBUS_TYPE_ARRAY)
        return;

    int rssi = -999;
    int mfg_hit = 0;

    DBusMessageIter arr;
    dbus_message_iter_recurse(&it, &arr);

    while (dbus_message_iter_get_arg_type(&arr) == DBUS_TYPE_DICT_ENTRY) {
        DBusMessageIter entry;
        dbus_message_iter_recurse(&arr, &entry);

        // key
        const char *key = NULL;
        if (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
            dbus_message_iter_get_basic(&entry, &key);
        }
        dbus_message_iter_next(&entry);

        if (!key || dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT) {
            dbus_message_iter_next(&arr);
            continue;
        }

        DBusMessageIter var;
        dbus_message_iter_recurse(&entry, &var);

        if (strcmp(key, "RSSI") == 0 && dbus_message_iter_get_arg_type(&var) == DBUS_TYPE_INT16) {
            dbus_int16_t v;
            dbus_message_iter_get_basic(&var, &v);
            rssi = (int)v;
        }

        if (strcmp(key, "ManufacturerData") == 0 && dbus_message_iter_get_arg_type(&var) == DBUS_TYPE_ARRAY) {
            DBusMessageIter dict;
            dbus_message_iter_recurse(&var, &dict);
            // dict entries
            while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
                if (parse_mfg_data(&dict, rssi)) {
                    mfg_hit = 1;
                    break;
                }
                dbus_message_iter_next(&dict);
            }
        }

        dbus_message_iter_next(&arr);
    }

    if (mfg_hit) {
        trigger_wakeup();
    }
}

static void start_discovery(DBusConnection *conn) {
    DBusError err;
    dbus_error_init(&err);

    DBusMessage *msg = dbus_message_new_method_call(
        "org.bluez",
        "/org/bluez/hci0",
        "org.bluez.Adapter1",
        "StartDiscovery");

    if (!msg)
        die("dbus_message_new_method_call failed");

    DBusMessage *reply = dbus_connection_send_with_reply_and_block(conn, msg, 5000, &err);
    dbus_message_unref(msg);

    if (dbus_error_is_set(&err)) {
        fprintf(stderr, "StartDiscovery error: %s\n", err.message);
        dbus_error_free(&err);
        return;
    }

    if (reply)
        dbus_message_unref(reply);

    fprintf(stdout, "[ble_wake] discovery started\n");
}

int main(void) {
    DBusError err;
    dbus_error_init(&err);

    DBusConnection *conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
    if (!conn) {
        fprintf(stderr, "dbus_bus_get: %s\n", err.message);
        return 1;
    }

    // 订阅 PropertiesChanged
    const char *rule = "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'";
    dbus_bus_add_match(conn, rule, &err);
    dbus_connection_flush(conn);

    if (dbus_error_is_set(&err)) {
        fprintf(stderr, "add_match error: %s\n", err.message);
        dbus_error_free(&err);
        return 1;
    }

    start_discovery(conn);

    while (1) {
        dbus_connection_read_write(conn, 1000);
        DBusMessage *msg = dbus_connection_pop_message(conn);
        if (!msg)
            continue;

        if (dbus_message_is_signal(msg, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
            process_properties_changed(msg);
        }

        dbus_message_unref(msg);
    }

    return 0;
}

8.3 唤醒动作脚本:wake_action.sh

#!/bin/bash
# /usr/local/bin/wake_action.sh
# 示例:命中后做一件可见的动作
# 真实产品:这里可以触发 systemd 进入目标服务,或写入一个状态文件。

logger -t ble_wake "WAKE command matched, do action"

echo 1 > /tmp/ble_wake_triggered

# 可选:唤醒后广播在线状态 30 秒
/usr/local/bin/camera_adv_online.sh &

9. Camera 醒来后广播 30 秒“在线状态”:可执行脚本

9.1 在线广播 payload 设计

把 CMD 改为 ONLINE(例如 0xB1),Target 填自己 ID:

  • Company: 0x1234
  • Product: 0x01
  • Target: MY_ID
  • CMD: 0xB1
  • Token: 0x66

9.2 脚本:camera_adv_online.sh

#!/bin/bash
set -e

COMPANY_L=0x34
COMPANY_H=0x12
PRODUCT=0x01
MYID=${MYID:-0x02}
CMD=0xB1
TOKEN=0x66

# start adv
hcitool cmd 0x08 0x0036 \
  0x00 0x13 0x00 0x90 0x01 0x00 0xc2 0x01 0x00 0x07 0x00 \
  0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x00 0x01 0x00 0x00

hcitool cmd 0x08 0x0037 \
  0x00 0x03 0x01 \
  0x02 0x01 0x06 \
  0x07 0xFF $COMPANY_L $COMPANY_H $PRODUCT $MYID $CMD $TOKEN

hcitool cmd 0x08 0x0039 0x01 0x01 0x00 0x00 0x00 0x00

sleep 30

# stop adv
hcitool cmd 0x08 0x0039 0x00 0x01 0x00 0x00 0x00 0x00

echo "[camera_adv_online] done"

10. 让 Camera 进入低功耗并保持 BLE 作为唤醒源:系统配置要点(Rockchip)

这里要区分:

  • 验证阶段:主机不进入最深睡眠,BlueZ 进程仍在跑。
  • 产品阶段:主机进入 suspend-to-RAM,BLE Controller 继续低占空比扫描,并通过 wake GPIO/IRQ 唤醒。

10.1 检查蓝牙设备是否允许 wakeup

# 先找到蓝牙相关设备路径(示例)
find /sys/devices -name wakeup -path '*bluetooth*' -o -path '*hci0*' 2>/dev/null

# 典型可设置项:
cat /sys/class/bluetooth/hci0/device/power/wakeup 2>/dev/null || true

# 如果存在,尝试 enable
echo enabled > /sys/class/bluetooth/hci0/device/power/wakeup 2>/dev/null || true

注:不同内核/控制器路径可能不同,关键是找到对应设备的 power/wakeup

10.2 进入 suspend 验证

# 写入 mem 让系统进入 suspend-to-RAM
echo mem > /sys/power/state

验证点:

  • 平板发 WAKE 广播
  • Camera 是否被唤醒(串口、日志、/tmp 文件变化、系统服务恢复)

10.3 推荐:systemd 管理扫描服务(开发阶段)

ble-wake.service
[Unit]
Description=BLE Wake Scanner
After=bluetooth.service
Wants=bluetooth.service

[Service]
Type=simple
ExecStart=/usr/local/bin/ble_wake_scanner
Restart=always
RestartSec=1

[Install]
WantedBy=multi-user.target

启用:

systemctl enable ble-wake.service
systemctl start ble-wake.service
journalctl -u ble-wake.service -f

11. 从“开发验证”走向“真正低功耗产品”:Controller 层过滤 + 唤醒中断

如果你要在 Camera 上做到更深的低功耗:

  • 不希望用户态进程一直运行
  • 不希望主机频繁被无关广播唤醒

那么建议:

  1. 把过滤下沉到 Controller 层

    • 只对特定 Manufacturer Data 上报/触发
    • 你已有的 vendor HCI(例如 0x3f 0x157 这类)就是这种方向
  2. 命中后通过硬件链路唤醒 SoC

    • Controller → Wake GPIO → PMIC/SoC Wake
  3. 加入防抖与冷却时间

    • 命中一次后 30 秒内忽略
    • 或连续命中 N 次才触发

11.1 防误唤醒的最低配策略

  • 固定协议字段(Company/Product/CMD/Target)
  • RSSI 阈值
  • Token(可升级为 rolling code)
  • 触发后 cooldown(例如 30 秒)

12. “4 Camera + 1 平板”落地步骤(一步一步,按最短路径验证)

Step 1:统一协议

  • Company:0x1234(示例)
  • Product:0x01(Camera)
  • CMD:WAKE=0xA5
  • Target:0xFF(全部)或 0x01~0x04(指定)

为每台 Camera 配置唯一 ID:

  • Camera A:0x01
  • Camera B:0x02
  • Camera C:0x03
  • Camera D:0x04

Step 2:在每台 Camera 部署 scanner

  • 编译 ble_wake_scanner.c/usr/local/bin/ble_wake_scanner
  • wake_action.sh(先写 /tmp 文件验证)
  • systemd 启动

Step 3:平板发送 WAKE 广播验证

  • 运行 tablet_adv_wake.sh 0xFF
  • Camera 应该全部命中并执行动作

Step 4:验证定向唤醒

  • tablet_adv_wake.sh 0x02 仅唤醒 Camera #2

Step 5:加入 RSSI 阈值/冷却

  • 调整 RSSI_THRESHOLD
  • 在触发后增加 30 秒忽略窗口

Step 6:进入 suspend 测试

  • Camera:echo mem > /sys/power/state
  • 平板发 WAKE
  • 观察 Camera 是否 resume

如果此步不稳定:说明系统的 wakeup source 或控制器侧配置还未打通,需要回到第 10/11 节做电源路径调试。


13. 常见问题与排查

13.1 我能 scan 到设备,但 payload 看不到

  • 使用 btmon 查看 HCI 报告
  • bluetoothctl 默认不展示完整 manufacturer data

13.2 扫描命中但系统不唤醒

  • 检查 /sys/.../power/wakeup 是否 enabled
  • 检查 suspend 模式是否为 s2ram
  • 检查控制器是否在 suspend 期间仍供电
  • 产品级:走 GPIO/IRQ 唤醒链路

13.3 谁都能唤醒的问题

  • 不要用 name/MAC
  • 必须用 manufacturer data 的固定字段
  • 加 token + RSSI + cooldown

13.4 hcitool 不存在

  • 新版系统可能移除 hcitool(bluez- deprecated)
  • 验证阶段可安装 bluez5-tools
  • 产品化建议写专用 HCI 程序或使用 mgmt/DBus API

14. 核心知识点总结(你应该真正记住的 10 条)

  1. Advertise 是发,Scan 是收;RSSI 永远由 Scan 端测量。
  2. 低功耗唤醒不需要连接,连接只会增加复杂度与功耗。
  3. 产品识别不要靠 name/MAC,要靠 Manufacturer Specific Data(0xFF)
  4. 广播协议字段要固定长度,便于硬件/控制器层过滤。
  5. 唤醒是“判定 + 触发”,不是“收到就醒”。
  6. RSSI 阈值可显著降低误唤醒概率。
  7. Token/rolling code/cooldown 是防误触与抗攻击的基础。
  8. Rockchip 上要验证 wakeup source 路径:power/wakeup + suspend/resume。
  9. 开发阶段可用 BlueZ D-Bus 解析;产品阶段建议下沉到 controller + GPIO。
  10. 4 Camera + 平板的最简单先跑通:Target=ALL → 再做 Target=ID。

15. 你可以直接复用的最小文件清单

  • Camera:

    • /usr/local/bin/ble_wake_scanner
    • /usr/local/bin/wake_action.sh
    • /usr/local/bin/camera_adv_online.sh
    • /etc/systemd/system/ble-wake.service
  • 平板:

    • tablet_adv_wake.sh

16. 结尾:一个“最短成功路径”的建议

工程落地时,优先顺序建议:

  1. 先在不进入最深睡眠的情况下跑通协议与过滤(btmon 看到 payload、scanner 命中)。
  2. 再把系统推进到 suspend-to-RAM,打通 wakeup source。
  3. 最后将过滤下沉到 Controller 层(vendor HCI),实现真正低功耗与高鲁棒。

视频教程请关注 B 站:“嵌入式 Jerry”


📺 B站视频讲解(Bilibili)https://www.bilibili.com/video/BV1k1C9BYEAB/

📘 《Yocto项目实战教程》京东购买链接Yocto项目实战教程


您可能感兴趣的与本文相关内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值