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; }
故障原因与修复方案
核心问题
- 蓝牙初始化被阻塞:
btstack_main中先调用了transfer()死循环,导致后续的蓝牙服务初始化代码完全没执行,蓝牙设备根本没启动,自然无法连接。 - 双核任务分配错误:
transfer()的死循环占用了核心0的主线程,蓝牙事件循环无法正常运行。 - 共享变量无同步:
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的手势预测正常输出结果
- 手势结果能通过BLE实时传输到手机
内容的提问来源于stack exchange,提问作者Ayush Mritunjay
相关产品推荐
相关产品推荐

