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

ESP32连接小米智能魔方触发heap_caps_free断言失败问题求助

ESP32连接小米智能魔方触发heap_caps_free断言失败(跨Arduino IDE版本问题)

问题描述

我用ESP32连接小米智能魔方,代码功能是根据魔方动作控制LED,参考视频编写。该代码在教师的Arduino IDE 1.8.19环境下能正常连接并读取魔方动作,但在我的Arduino IDE 2.0.4及另一台PC的1.8.5环境下,设备连接并开始读取动作时触发断言失败错误:

assert failed: heap_caps_free heap_caps.c:360 (heap != NULL && "free() target pointer is outside heap areas")

之后ESP32会重启并重复出现该错误,请求解决。

完整串口输出

Guru Meditation Error: Core 0 panic'ed (LoadProhibited). Exception was unhandled.

Core 0 register dump:
PC : 0x4008129e PS : 0x00060b30 A0 : 0x800815c5 A1 : 0x3ffb1f70
A2 : 0x00000000 A3 : 0x00000000 A4 : 0x00000000 A5 : 0x00000000
A6 : 0x00000000 A7 : 0x00000000 A8 : 0x00000000 A9 : 0x3ffb1f30
A10 : 0x00000000 A11 : 0x00000000 A12 : 0x00000000 A13 : 0x00000000
A14 : 0x00000000 A15 : 0x00000000 SAR : 0x0000001a EXCCAUSE: 0x0000001c
EXCVADDR: 0x00000000 LBEG : 0x4000c2e0 LEND : 0x4000c2f6 LCOUNT : 0x00000000

Backtrace: 0x4008129b:0x3ffb1f70 0x400815c2:0x3ffb1f90 0x4008163d:0x3ffb1fb0 0x4008754d:0x3ffb1fd0 0x400875b9:0x3ffb1ff0 0x400d1095:0x3ffb2010 0x400d0f6d:0x3ffb2030 0x400d0356:0x3ffb2050 0x400d03d5:0x3ffb2070 0x400d20a6:0x3ffb2090 0x400d2106:0x3ffb20b0 0x400d213a:0x3ffb20d0 0x40087095:0x3ffb20f0

Rebooting...
ets Jun 8 2016 00:22:57

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:1344
load:0x40078000,len:13864
load:0x40080400,len:3608
entry 0x400805f0
assert failed: heap_caps_free heap_caps.c:360 (heap != NULL && "free() target pointer is outside heap areas")

Backtrace: 0x4008129b:0x3ffb1f70 0x400815c2:0x3ffb1f90 0x4008163d:0x3ffb1fb0 0x4008754d:0x3ffb1fd0 0x400875b9:0x3ffb1ff0 0x400d1095:0x3ffb2010 0x400d0f6d:0x3ffb2030 0x400d0356:0x3ffb2050 0x400d03d5:0x3ffb2070 0x400d20a6:0x3ffb2090 0x400d2106:0x3ffb20b0 0x400d213a:0x3ffb20d0 0x40087095:0x3ffb20f0

代码示例

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>

#define LED_PIN 2

// 小米魔方的服务UUID和特征UUID
static BLEUUID serviceUUID("0000fee0-0000-1000-8000-00805f9b34fb");
static BLEUUID charUUID("0000fee1-0000-1000-8000-00805f9b34fb");

static boolean doConnect = false;
static boolean connected = false;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static BLEAdvertisedDevice* myDevice;

void notifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) {
  // 解析魔方动作
  switch(pData[0]) {
    case 0x01:
      digitalWrite(LED_PIN, HIGH);
      Serial.println("魔方被旋转");
      break;
    case 0x02:
      digitalWrite(LED_PIN, LOW);
      Serial.println("魔方被翻转");
      break;
    // 其他动作处理...
  }
}

class MyClientCallback : public BLEClientCallbacks {
  void onConnect(BLEClient* pclient) {
    connected = true;
    Serial.println("已连接魔方");
  }

  void onDisconnect(BLEClient* pclient) {
    connected = false;
    Serial.println("断开连接");
  }
};

bool connectToServer() {
  Serial.print("连接到 ");
  Serial.println(myDevice->getAddress().toString().c_str());

  BLEClient*  pClient = BLEDevice::createClient();
  pClient->setClientCallbacks(new MyClientCallback());

  // 连接到设备
  if (!pClient->connect(myDevice)) {
    Serial.println("连接失败");
    return false;
  }
  Serial.println("连接成功");

  // 获取服务
  BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
  if (pRemoteService == nullptr) {
    Serial.println("无法找到服务");
    pClient->disconnect();
    return false;
  }
  Serial.println("找到服务");

  // 获取特征
  pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
  if (pRemoteCharacteristic == nullptr) {
    Serial.println("无法找到特征");
    pClient->disconnect();
    return false;
  }
  Serial.println("找到特征");

  // 启用通知
  if(pRemoteCharacteristic->canNotify())
    pRemoteCharacteristic->registerForNotify(notifyCallback);

  return true;
}

void advertiseDeviceFound(BLEAdvertisedDevice advertisedDevice) {
  if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
    BLEDevice::getScan()->stop();
    myDevice = new BLEAdvertisedDevice(advertisedDevice);
    doConnect = true;
    Serial.println("发现小米魔方");
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);

  Serial.println("初始化BLE客户端");
  BLEDevice::init("ESP32-Cube-Controller");

  BLEScan* pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new BLEAdvertisedDeviceCallbacks() {
    void onResult(BLEAdvertisedDevice advertisedDevice) {
      advertiseDeviceFound(advertisedDevice);
    }
  });
  pBLEScan->setActiveScan(true);
  pBLEScan->start(5);
}

void loop() {
  if (doConnect == true) {
    if (connectToServer()) {
      Serial.println("成功连接,开始接收动作");
    } else {
      Serial.println("连接失败,重新扫描");
      BLEDevice::getScan()->start(5);
    }
    doConnect = false;
  }

  delay(1000);
}

解决方案

1. 统一ESP32核心版本

问题核心是不同IDE环境下ESP32 Arduino核心版本不一致,导致内存管理逻辑差异。教师的1.8.19环境使用的核心版本较新,而你的1.8.5和2.0.4环境核心版本偏旧或匹配异常:

  • 打开Arduino IDE,进入文件 > 首选项,确认“附加开发板管理器网址”包含ESP32核心地址:https://dl.espressif.com/dl/package_esp32_index.json
  • 进入工具 > 开发板 > 开发板管理器,搜索“ESP32”,安装与教师环境相同的核心版本(推荐2.0.10及以上稳定版)

2. 修复非法内存释放/泄漏问题

代码中存在多处用new创建对象但未正确释放的情况,旧版本核心会触发非法内存释放断言:

  • 将回调对象改为全局实例,避免动态分配内存:
// 全局定义回调对象
MyClientCallback clientCallback;

class MyAdvertisedCallback : public BLEAdvertisedDeviceCallbacks {
  void onResult(BLEAdvertisedDevice advertisedDevice) {
    advertiseDeviceFound(advertisedDevice);
  }
};
MyAdvertisedCallback advertisedCallback;

// 在connectToServer中替换原回调设置
pClient->setClientCallbacks(&clientCallback);

// 在setup中替换扫描回调设置
pBLEScan->setAdvertisedDeviceCallbacks(&advertisedCallback);
  • 释放myDevice动态分配的内存:
bool connectToServer() {
  // ... 现有代码 ...
  bool success = false;
  if (pClient->connect(myDevice)) {
    // ... 连接成功逻辑 ...
    success = true;
  }
  // 释放动态分配的myDevice
  delete myDevice;
  myDevice = nullptr;
  return success;
}

3. 调整IDE编译选项

  • 在Arduino IDE 2.0.4中,进入工具 > 核心调试级别,选择“错误”,减少调试输出对内存的占用
  • 若ESP32模块支持外部SPIRAM,开启工具 > 内存分配方案 > 使用外部SPIRAM,缓解内存压力

4. 验证测试

修改代码并更新核心版本后,重新编译上传,观察串口输出是否还会触发断言错误。若问题仍存在,可在notifyCallback中添加指针合法性检查,避免非法内存访问。

内容的提问来源于stack exchange,提问作者Pedro Milan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 15:07:09