You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pi Pico W双核+BLE传输Edge Impulse手势预测蓝牙连接故障排查

Pi Pico W 双核+BLE手势预测传输故障排查与修复

我是大一学生,用Pi-Pico C++ SDK完成了基于Edge Impulse的手势预测代码,现在想利用Pi Pico W的双核能力,通过BLE把手势预测结果传到手机。相关资料少,BLE和双核集成难度大。我修改了BT-Stack的SPP代码实现蓝牙文本传输,但和双核上的手势预测代码集成后无法正常工作:gesture()函数能在核心1运行,但蓝牙连不上。


原代码

main.cpp

#include<stdio.h>
#include "pico/stdlib.h"
#include "pico/time.h"
#include "hardware/i2c.h"
#include "hardware/gpio.h"
#include "ei_run_classifier.h"
#include "hardware/pwm.h"
#include "hardware/pio.h"

#include "pico/cyw43_arch.h"
#include "pico/stdlib.h"
#include "btstack_run_loop.h"

#include "pico/multicore.h"

#define SCL 1
#define SDA 0

#define I2C_PORT i2c0

void mpu6050_reset();
void transfer(int a);
void gesture(void);
void mpu6050_read(int16_t accelerometer[3]);
int btstack_main(int argc, const char * argv[]);

static bool debug_nn = false;
volatile bool timer0_occur=false,timer1_occur=false;
const uint LED_PIN = 15;

void gesture(void){
   
    gpio_init(LED_PIN);
    gpio_set_dir(LED_PIN, GPIO_OUT);

    //Initialize I2C port 0 and configuring Pins 0 and 1 for MPU6050
    i2c_init(I2C_PORT,100000);
    gpio_set_function(SCL,GPIO_FUNC_I2C);
    gpio_set_function(SDA,GPIO_FUNC_I2C);
    gpio_pull_up(SCL);
    gpio_pull_up(SDA);
    mpu6050_reset();

    ei_impulse_result_t result = {0};
    int16_t accelerometer[3];

    if (EI_CLASSIFIER_RAW_SAMPLES_PER_FRAME != 3) {
        ei_printf("ERR: EI_CLASSIFIER_RAW_SAMPLES_PER_FRAME should be equal to 3 (the 3 sensor axes)\n");
    }

    while (true) 
    {
        gpio_put(LED_PIN,1);  //Indicating wait operation
        ei_printf("\nStarting inferencing in 1 seconds...\n");
        sleep_ms(1000);
        gpio_put(LED_PIN,0);
        ei_printf("Sampling...\n");

        // Allocate a buffer here for the values we'll read from the IMU
        float buffer[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE] = { 0 };

        for (size_t ix = 0; ix < EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE; ix += 3) {
            uint64_t next_tick = ei_read_timer_us() + (EI_CLASSIFIER_INTERVAL_MS * 1000);
            mpu6050_read(accelerometer);
            buffer[ix + 0]= accelerometer[0];
            buffer[ix + 1]= accelerometer[1];
            buffer[ix + 2]= accelerometer[2];
            sleep_us(next_tick - ei_read_timer_us());
        }

        // Turn the raw buffer in a signal which we can the classify
        signal_t signal;
        int err = numpy::signal_from_buffer(buffer, EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE, &signal);
        if (err != 0) {
            ei_printf("Failed to create signal from buffer (%d)\n", err);
            break;
        }

        // Run the classifier
        ei_impulse_result_t result = { 0 };
        err = run_classifier(&signal, &result, debug_nn);
        if (err != EI_IMPULSE_OK) {
            ei_printf("ERR: Failed to run classifier (%d)\n", err);
            break;
        }
        
        if(result.classification[0].value>=0.8)
        {
            ei_printf("Prediction: Circle\n");
            multicore_fifo_push_blocking(0);
        }
        else if(result.classification[1].value>=0.8)
        {
            ei_printf("Prediction: Idle\n");
            multicore_fifo_push_blocking(1);
        }
        else if(result.classification[2].value>=0.8)
        {
            ei_printf("Prediction: Left-Right\n");
            multicore_fifo_push_blocking(2);
        }
        else if(result.classification[3].value>=0.8)
        {
            ei_printf("Prediction: Up-Down\n");
            multicore_fifo_push_blocking(3);
        }
        else
        {
            ei_printf("Error 404\n");
        }

        #if EI_CLASSIFIER_HAS_ANOMALY == 1
        ei_printf("    anomaly score: %.3f\n", result.anomaly);
        #endif
    }
}
    
int main()
{
    stdio_init_all();

    multicore_launch_core1(gesture);

    if (cyw43_arch_init()) {
        printf("cyw43_arch_init() failed.\n");
        return -1;
    }

    // run the app
    btstack_main(0, NULL);
    btstack_run_loop_execute();

    return 0;
}

蓝牙代码

/*
 * Simple SPP Server - Send Static Text over Bluetooth
 */

#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pico/multicore.h"
 
#include "btstack.h"

#define RFCOMM_SERVER_CHANNEL 1

void transfer();
static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);

static uint16_t rfcomm_channel_id;
static uint8_t  spp_service_buffer[150];
static char lineBuffer[30];
static btstack_packet_callback_registration_t hci_event_callback_registration;

static const char* text_message = "Bluetooth Initialized";

void transfer(){
    while(true){
        uint32_t a = multicore_fifo_pop_blocking();
        if(a == 0){
            snprintf(lineBuffer, sizeof(lineBuffer), "Circle");
            printf("%s", lineBuffer);
            rfcomm_request_can_send_now_event(rfcomm_channel_id);
        }
        if(a == 1){
            snprintf(lineBuffer, sizeof(lineBuffer), "Idle");
            printf("%s", lineBuffer);
            rfcomm_request_can_send_now_event(rfcomm_channel_id);
        }
        if(a == 2){
            snprintf(lineBuffer, sizeof(lineBuffer), "Left-Right");
            printf("%s", lineBuffer);
            rfcomm_request_can_send_now_event(rfcomm_channel_id);
        }
        if(a == 3){
            snprintf(lineBuffer, sizeof(lineBuffer), "Up-Down");
            printf("%s", lineBuffer);
            rfcomm_request_can_send_now_event(rfcomm_channel_id);
        }
    }
}

static void spp_service_setup(void) {
    // register for HCI events
    hci_event_callback_registration.callback = &packet_handler;
    hci_add_event_handler(&hci_event_callback_registration);

    l2cap_init();

#ifdef ENABLE_BLE
    // Initialize LE Security Manager. Needed for cross-transport key derivation
    sm_init();
#endif

    rfcomm_init();
    rfcomm_register_service(packet_handler, RFCOMM_SERVER_CHANNEL, 0xffff);  // reserved channel, mtu limited by l2cap

    // init SDP, create record for SPP and register with SDP
    sdp_init();
    memset(spp_service_buffer, 0, sizeof(spp_service_buffer));
    spp_create_sdp_record(spp_service_buffer, 0x10001, RFCOMM_SERVER_CHANNEL, "PicoW RP2040");
    sdp_register_service(spp_service_buffer);
    printf("SDP service record size: %u\n", de_get_len(spp_service_buffer));
}

static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
    UNUSED(channel);

    bd_addr_t event_addr;
    uint8_t   rfcomm_channel_nr;
    uint16_t  mtu;

    switch (packet_type) {
        case HCI_EVENT_PACKET:
            switch (hci_event_packet_get_type(packet)) {
                case RFCOMM_EVENT_CHANNEL_OPENED:
                    if (rfcomm_event_channel_opened_get_status(packet)) {
                        printf("RFCOMM channel open failed, status %u\n", rfcomm_event_channel_opened_get_status(packet));
                    } else {
                        rfcomm_channel_id = rfcomm_event_channel_opened_get_rfcomm_cid(packet);
                        mtu = rfcomm_event_channel_opened_get_max_frame_size(packet);
                        printf("RFCOMM channel open succeeded. New RFCOMM Channel ID %u, max frame size %u\n", rfcomm_channel_id, mtu);
                        // send the text message once the channel is opened
                        rfcomm_send(rfcomm_channel_id, (uint8_t*) text_message, (uint16_t) strlen(text_message));
                    }
                    break;
                case RFCOMM_EVENT_CAN_SEND_NOW:
                    rfcomm_send(rfcomm_channel_id, (uint8_t*) lineBuffer, (uint16_t) strlen(lineBuffer));  
                    break;
                
                default:
                    break;
            }
            break;

        default:
            break;
    }
}

int btstack_main(int argc, const char * argv[]) {
    (void)argc;
    (void)argv;

    transfer();
    spp_service_setup();
    
    gap_discoverable_control(1);
    gap_ssp_set_io_capability(SSP_IO_CAPABILITY_DISPLAY_YES_NO);
    gap_set_local_name("PicoW RP2040");

    // turn on!
    hci_power_control(HCI_POWER_ON);
    
    return 0;
}

故障原因与修复方案

核心问题

  1. 蓝牙初始化被阻塞:btstack_main中先调用了transfer()死循环,导致后续的蓝牙服务初始化代码完全没执行,蓝牙设备根本没启动,自然无法连接。
  2. 双核任务分配错误:transfer()的死循环占用了核心0的主线程,蓝牙事件循环无法正常运行。
  3. 共享变量无同步:lineBuffer是全局变量,被核心0和蓝牙回调函数交叉访问,存在线程安全问题。

修复步骤

1. 重构蓝牙初始化流程,移除阻塞代码

修改btstack_main,去掉transfer()调用,改用BT-Stack的定时器轮询FIFO消息,避免阻塞初始化:

#include "pico/mutex.h"

static uint16_t rfcomm_channel_id = 0;
static mutex_t line_buffer_mutex;
static char lineBuffer[30];

// 新增FIFO消息处理任务
static void fifo_process_task(struct btstack_timer_source *ts){
    if(multicore_fifo_rvalid()){
        uint32_t a = multicore_fifo_pop_blocking();
        mutex_enter_blocking(&line_buffer_mutex);
        if(a == 0){
            snprintf(lineBuffer, sizeof(lineBuffer), "Circle");
        } else if(a == 1){
            snprintf(lineBuffer, sizeof(lineBuffer), "Idle");
        } else if(a == 2){
            snprintf(lineBuffer, sizeof(lineBuffer), "Left-Right");
        } else if(a == 3){
            snprintf(lineBuffer, sizeof(lineBuffer), "Up-Down");
        }
        mutex_exit(&line_buffer_mutex);
        
        // 仅当RFCOMM通道已建立时发送消息
        if(rfcomm_channel_id != 0){
            rfcomm_request_can_send_now_event(rfcomm_channel_id);
        }
    }
    // 重新注册定时器,持续轮询
    btstack_run_loop_set_timer(ts, 10); // 每10ms轮询一次
    btstack_run_loop_add_timer(ts);
}

static btstack_timer_source fifo_timer;

int btstack_main(int argc, const char * argv[]) {
    (void)argc;
    (void)argv;

    // 初始化互斥锁
    mutex_init(&line_buffer_mutex);

    spp_service_setup();
    
    gap_discoverable_control(1);
    gap_ssp_set_io_capability(SSP_IO_CAPABILITY_DISPLAY_YES_NO);
    gap_set_local_name("PicoW RP2040");

    // 启动FIFO消息处理定时器
    btstack_timer_source_init(&fifo_timer, fifo_process_task);
    btstack_run_loop_set_timer(&fifo_timer, 10);
    btstack_run_loop_add_timer(&fifo_timer);

    hci_power_control(HCI_POWER_ON);
    
    return 0;
}

2. 修复蓝牙回调的线程安全问题

修改packet_handler中的发送逻辑,添加互斥锁保护:

case RFCOMM_EVENT_CAN_SEND_NOW:
    mutex_enter_blocking(&line_buffer_mutex);
    rfcomm_send(rfcomm_channel_id, (uint8_t*) lineBuffer, (uint16_t) strlen(lineBuffer));  
    mutex_exit(&line_buffer_mutex);
    break;

3. 调整main.cpp的核心启动顺序(可选优化)

确保蓝牙硬件初始化完成后再启动核心1,避免资源冲突:

int main()
{
    stdio_init_all();

    if (cyw43_arch_init()) {
        printf("cyw43_arch_init() failed.\n");
        return -1;
    }

    // 先完成蓝牙硬件初始化,再启动核心1的手势预测任务
    multicore_launch_core1(gesture);
    
    btstack_main(0, NULL);
    btstack_run_loop_execute();

    cyw43_arch_deinit();
    return 0;
}

修复后验证点

  1. 蓝牙设备可被手机搜索到并连接
  2. 核心1的手势预测正常输出结果
  3. 手势结果能通过BLE实时传输到手机

内容的提问来源于stack exchange,提问作者Ayush Mritunjay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 22:33:10