我晕,一个低级错误导致我DEBUG两天(std::string c_str()的问题)

本文讲述了作者在使用第三方库处理RO素材时遇到的堆损坏问题。通过加入特定代码进行调试,最终定位到了问题根源在于不当使用std::string的c_str()方法。

起因是这样的,为了方便读取RO里的素材,我在OPenRO里加入了一个第三方库,他的作用主要就是负责提取RO素材数据,并把他们放在heap里,程序退出他会自动释放。

但是莫名其妙的问题随之而来了:每次程序退出都会弹窗提示:“******,其原因可能是堆被损坏,这也说明****加载的Dll可能有问题”。看见这个,我第一反应是Dll里分配的内存在程序里释放时,Dll与exe使用了不同的C运行时库。但是我使用的这个第三方库根本就是一个静态lib啊,而且使用的C运行时库版本绝对是一样的。

我就郁闷了,剩下只有一种解释,这个lib有问题,可能其中的处理会导致堆损坏,比如越界写入,delete不存在的内存等。于是我就抱着找出这个BUG的信念,从头到尾把这个库给看了一遍。

一天完了,一无所获。

第二天,发现了更诡异的问题,我用这个第三方库加载了不同的素材,程序退出依然弹窗提示,但是从调试堆栈窗口看到的结果却不一样,显示的是我自己的模块堆损坏。这样,我又把自己的程序检查了一遍,还是没发现问题。。。(其实后来其实发现这个问题也是理所当然,因为整个堆损坏了嘛,可能覆盖了其他内存块)

我开始怀疑是我WIN7下不兼容VS2005的问题。又难得转成VS2008,正准备放弃这个BUG待以后解决时,采用了Google到的一个方法,在程序代码中加入:

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_DELAY_FREE_MEM_DF | _CRTDBG_CHECK_EVERY_1024_DF | _CRTDBG_CHECK_CRT_DF);


_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_WNDW);

这样程序就回提示你代码哪里的操作会导致堆损坏,比如越界等。虽然我试验提示的区域还是反汇编,但是从调试堆栈我找到了堆损坏的源头。看了差点没把我气死,这个错误在金山实习时就被BOSS指出来过,虽然当时我的代码并没有触发这个BUG。。。。

void SomeFunction(const char* str);

std::string str;
SomeFunction(str.c_str());

看似非常正常的一段代码,但是,显然,str.c_str()的生存期是由str管理的,一旦str销毁,c_str()也就销毁了,如果SomeFunction继续处理这个指针,那就不晓得损坏的是堆里面的哪块内存了,结果就是导致一系列诡异的BUG,让你很难找到错误的源头。所以,建议,使用string的c_str()函数时,需要非常谨慎。

发现经常DEBUG,有些问题还是很通用的,所以建立一个DEBUG标签,记录一些DEBUG经验。

#include “BLE_master.h” #include <Arduino.h> // 静态成员定义 const char* BLEMaster::DEVICE_NAME = “GMD1032-BLE-Slave”; const char* BLEMaster::TARGET_SERVICE_UUID = “4fafc201-1fb5-459e-8fcc-c5c9c331914b”; const char* BLEMaster::TARGET_CHAR_UUID = “beb5483e-36e1-4688-b7f5-ea07361b26a8”; bool BLEMaster::connected = false; NimBLEClient* BLEMaster::pClient = nullptr; BLERemoteCharacteristic* BLEMaster::pRemoteCharacteristic = nullptr; NimBLEAdvertisedDevice* BLEMaster::targetDevice = nullptr; void (BLEMaster::dataCallback)(const char) = nullptr; BLEMaster::BLEMaster() {} void BLEMaster::begin() { Serial.println(“Initializing BLE Master (NimBLE)…”); NimBLEDevice::init(""); NimBLEScan* pScan = NimBLEDevice::getScan(); pScan->setScanCallbacks(new AdvertisedDeviceCallback()); // ✅ 固定写法 pScan->setActiveScan(true); pScan->start(5000, false); // 扫描20秒 } void BLEMaster::onDataReceived(void (callback)(const char)) { dataCallback = callback; } void BLEMaster::loop() { if (connected && pClient && pClient->isConnected()) { return; } if (targetDevice != nullptr && !connected) { Serial.printf("Connecting to: %s\n", targetDevice->getName().c_str()); // 使用工厂方法创建 client pClient = NimBLEDevice::createClient(); pClient->setClientCallbacks(new ClientCallback()); if (!pClient->connect(targetDevice)) { Serial.println("Connection failed!"); pClient->disconnect(); // 不要 delete pClient = nullptr; return; } Serial.println("Connected! Discovering service..."); BLERemoteService* pRemoteService = pClient->getService(NimBLEUUID(TARGET_SERVICE_UUID)); if (pRemoteService == nullptr) { Serial.println("Service not found"); pClient->disconnect(); pClient = nullptr; return; } pRemoteCharacteristic = pRemoteService->getCharacteristic(NimBLEUUID(TARGET_CHAR_UUID)); if (pRemoteCharacteristic == nullptr) { Serial.println("Characteristic not found"); pClient->disconnect(); pClient = nullptr; return; } // 请求大 MTU requestMaxMTU(); // 订阅通知 使用 subscribe() if (pRemoteCharacteristic->canNotify()) { bool subscribed = pRemoteCharacteristic->subscribe(true, [](BLERemoteCharacteristic* pBLERC, uint8_t* pData, size_t length, bool isNotify) { char jsonStr[512] = {0}; size_t copyLen = min(length, sizeof(jsonStr) - 1); memcpy(jsonStr, pData, copyLen); if (dataCallback) { dataCallback((const char*)jsonStr); } else { Serial.printf("[BLE] Received (%u bytes): %.*s\n", length, copyLen, jsonStr); } }); if (subscribed) { Serial.println("Notify enabled."); } else { Serial.println("Subscribe failed!"); } } else { Serial.println("Characteristic does not support notification."); } connected = true; } } // 回调实现 // ==================== AdvertisedDeviceCallback 实现 ==================== void BLEMaster::AdvertisedDeviceCallback::onResult(const NimBLEAdvertisedDevice* advertisedDevice) { // 转换为非 const(如果需要保存副本) if (strcmp(advertisedDevice->getName().c_str(), BLEMaster::DEVICE_NAME) == 0) { Serial.printf(“Found: %s [RSSI=%d]\n”, advertisedDevice->getName().c_str(), advertisedDevice->getRSSI()); // 删除旧设备 if (BLEMaster::targetDevice) { delete BLEMaster::targetDevice; } // 创建副本(注意:不能直接赋值 const 对象) BLEMaster::targetDevice = new NimBLEAdvertisedDevice(*advertisedDevice); // 停止扫描 NimBLEDevice::getScan()->stop(); } } // ==================== ClientCallback 实现 ==================== void BLEMaster::ClientCallback::onConnect(NimBLEClient* pClient) { Serial.println(“Device connected”); } void BLEMaster::ClientCallback::onDisconnect(NimBLEClient* pClient, int reason) { char addrStr[64] = {0}; snprintf(addrStr, sizeof(addrStr), “%s”, pClient->getPeerAddress().toString().c_str()); Serial.printf(“🔌 Disconnected from: %s, reason=%d\n”, addrStr, reason); BLEMaster::connected = false; BLEMaster::pRemoteCharacteristic = nullptr; BLEMaster::pClient = nullptr; // 注意:不要 delete pClient,库会自动清理 } void BLEMaster::ClientCallback::onMTUChange(NimBLEClient* pClient, uint16_t MTU) { Serial.printf(“MTU 已更新为: %u\n”, MTU); } // 请求 MTU void BLEMaster::requestMaxMTU() { if (pClient && pClient->isConnected()) { uint16_t mtu = pClient->getMTU(); Serial.printf(“当前 MTU: %u\n”, mtu); // 发起 MTU 协商请求(异步操作) if (!pClient->exchangeMTU()) { Serial.println("MTU exchange failed!"); } else { Serial.println("已发送 MTU 请求 = 512"); } } } 上述为BLE_master.cpp #include “main.h” #include <WiFi.h> #include “esp_uart_driver.h” #include “uart_msg_analy.h” #include “network_app.h” #include “spi_slave_server.h” #include “BLE_master.h” uint32_t loop_test_timer_cnt=0; #define UART1_TX_PIN 4 #define UART1_RX_PIN 5 BLEMaster bleMaster; // 自定义处理接收到的数据 void handleData(const char* data) { Serial.println(“[JSON] 收到数据:); Serial.println(data); // 可在此处添加 JSON 解析逻辑(例如用 ArduinoJson) // 示例:提取 cell 和 temp /* #include <ArduinoJson.h> StaticJsonDocument<512> doc; DeserializationError err = deserializeJson(doc, data); if (!err) { JsonArray cell = doc["cell"]; float temp = doc["temp"]; Serial.printf("温度: %.2f°C\n", temp); for (int i = 0; i < 16; i++) { Serial.printf("CELL%d: %.3fV ", i+1, cell[i]); } Serial.println(); } else { Serial.printf("JSON 解析失败: %s\n", err.c_str()); } */ } void setup() { delay(100); // 让 ROM 日志输出完成 int httpCode; uart0_init(115200, (void *)0);// 初始化主串口 // 需要放在Setup里初始化 uart1_init(115200, UART1_RX_PIN,UART1_TX_PIN,(void *)uart1_msg_analy);// 配置第二个串口 // 初始化随机数生成器 esp_random(); // 初始化随机数生成器 //wifi_app_init();// 启动WiFi功能 //spi_slave_server_init();//初始化SPI从机模式 //Serial.printf(“spi project done… %s %s\r\n”, DATE, TIME);//打印初始化完成信息 bleMaster.begin(); bleMaster.onDataReceived(handleData); // 设置回调 } int test_cnt = 0; char receivedChar = 0; void loop() { uart_process(); bleMaster.loop(); // 持续检查连接状态 delay(1000); } boolean doDelayMillisTime(uint32_t interval, uint32_t *previousMillis, boolean state)//非阻塞式定时器;state:状态翻转标志 { uint32_t currentMillis = (uint32_t)SYSTEM_TIMER_MS();//获取系统时间 if (currentMillis - *previousMillis >= interval)// interval 定时间隔(ms);previousMillis 存储上次触发时间的指针 { *previousMillis = currentMillis; state = !state; } return state; } void printf_log_hex(char *hex_name, uint8_t *data, uint32_t len)//十六进制数据打印工具 { Serial.printf(“%s ,len is:%d \r\n”, hex_name, len); for (int i = 0; i < len; i++) { Serial.printf(“%02x “, data[i]);//单字节总显示2位 } Serial.printf(”\r\n”);//保证终端正确换行 } 上述为main.cpp #include <SPI.h> #include <NimBLEDevice.h> // 替换 BLEDevice.h #include <NimBLE2904.h> // 替换 BLE2902.h #include <Arduino.h> // ==================== 引脚定义 ==================== #define SCK_PIN 13 #define MISO_PIN 11 #define MOSI_PIN 14 #define CS_PIN 12 // ==================== 启用调试模式 ==================== #define DEBUG_GMD1032 // ==================== GMD1032 命令列表 ==================== const uint16_t CMD_START_MEASUREMENT = 0x2A16; const uint16_t CMD_CLEAR_ADC_RESULTS = 0x243C; const uint16_t CMD_READ_CFG0 = 0xC08E; const uint16_t CMD_READ_STS0 = 0xC9B1; // CELL 分组命令 const uint16_t CMD_READ_CELL_1_3 = 0xD0FE; const uint16_t CMD_READ_CELL_4_6 = 0xD1F9; const uint16_t CMD_READ_CELL_7_9 = 0xD2F0; const uint16_t CMD_READ_CELL_10_12 = 0xD3F7; const uint16_t CMD_READ_CELL_13_15 = 0xD4E2; const uint16_t CMD_READ_CELL_16_18 = 0xD5E5; // DIS 分组命令 const uint16_t CMD_READ_DIS_1_3 = 0xD6EC; const uint16_t CMD_READ_DIS_4_6 = 0xD7EB; const uint16_t CMD_READ_DIS_7_9 = 0xD8C6; const uint16_t CMD_READ_DIS_10_12 = 0xD9C1; const uint16_t CMD_READ_DIS_13_15 = 0xDAC8; const uint16_t CMD_READ_DIS_16_18 = 0xDBCF; // 其他测量命令 const uint16_t CMD_READ_VREF2_V3 = 0xDFD3; const uint16_t CMD_READ_V5_DIETEMP_VSTK = 0xE06E; // ==================== SPI 设置 ==================== SPISettings spiSettings(1000000, MSBFIRST, SPI_MODE3); // 1MHz, Mode3 (CPOL=1, CPHA=1) // ==================== BLE 配置 ==================== #define SERVICE_UUID “4fafc201-1fb5-459e-8fcc-c5c9c331914b” #define CHARACTERISTIC_UUID “beb5483e-36e1-4688-b7f5-ea07361b26a8” NimBLECharacteristic* pCharacteristic = nullptr; // 改为 NimBLE 类型 bool deviceConnected = false; // 共享数据缓冲区 float sharedCellVoltages[16] = {0}; float sharedDisVoltages[16] = {0}; float sharedVref2 = 0.0f, sharedV3 = 0.0f, sharedV5 = 0.0f, sharedDieTemp = 0.0f, sharedVstk = 0.0f; portMUX_TYPE sensorMutex = portMUX_INITIALIZER_UNLOCKED; // 保护共享数据 // ==================== 回调类声明 ==================== class MyServerCallbacks : public NimBLEServerCallbacks { // 正确签名:使用 NimBLEConnInfo& void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) override { deviceConnected = true; Serial.println(“设备接入~”); } // 正确签名:包含 connInfo 和 reason void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) override { deviceConnected = false; Serial.print("设备断开~ 原因代码: "); Serial.println(reason); pServer->startAdvertising(); // 重新开始广播 } }; class MyCharacteristicCallbacks : public NimBLECharacteristicCallbacks { // 推荐加 const,并保留 override void onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) override { std::string value = pCharacteristic->getValue(); if (!value.empty()) { Serial.printf(“📥 客户端 %s 写入数据: %s\n”, std::string(connInfo.getAddress()).c_str(), // 正确调用 value.c_str()); } } }; // ==================== 辅助函数:打印二进制 ==================== void printBinary(uint8_t b) { for (int i = 7; i >= 0; i–) { Serial.write((b & (1 << i)) ? ‘1’ : ‘0’); } } // ==================== 唤醒芯片 ==================== void wakeUpGMD1032() { SPI.beginTransaction(spiSettings); digitalWrite(CS_PIN, LOW); delayMicroseconds(10); SPI.transfer(0x00); digitalWrite(CS_PIN, HIGH); SPI.endTransaction(); delay(5); } // ==================== 发送命令 ==================== bool sendCommand(uint16_t command) { SPI.beginTransaction(spiSettings); digitalWrite(CS_PIN, LOW); SPI.transfer16(command); digitalWrite(CS_PIN, HIGH); SPI.endTransaction(); return true; } // ==================== 解析逆序字段 ==================== void parseReverseFields(uint8_t* buffer, uint16_t* results, int count) { for (int i = 0; i < count; i++) { int bufIdx = i * 2; results[count - 1 - i] = (buffer[bufIdx] << 8) | buffer[bufIdx + 1]; } } // ==================== 读取所有 CELL 电压 ==================== bool readAllCellVoltages(float* cellVoltages) { const uint16_t cmds[] = { CMD_READ_CELL_1_3, CMD_READ_CELL_4_6, CMD_READ_CELL_7_9, CMD_READ_CELL_10_12, CMD_READ_CELL_13_15, CMD_READ_CELL_16_18 }; const int groupCount = 6; const int perGroup[] = {3, 3, 3, 3, 3, 3}; SPI.beginTransaction(spiSettings); for (int g = 0; g < groupCount; g++) { digitalWrite(CS_PIN, LOW); SPI.transfer16(cmds[g]); uint8_t buffer[8]; for (int i = 0; i < 8; i++) { buffer[i] = SPI.transfer(0x00); } digitalWrite(CS_PIN, HIGH); uint16_t raw[3]; parseReverseFields(buffer, raw, perGroup[g]); for (int i = 0; i < perGroup[g]; i++) { int idx = g * 3 + i; if (idx >= 16) continue; float voltage = (raw[i] - 10240) * 0.0001f; cellVoltages[idx] = voltage; } } SPI.endTransaction(); return true; } // ==================== 读取所有 DIS 电压 ==================== bool readAllDisVoltages(float* disVoltages) { const uint16_t cmds[] = { CMD_READ_DIS_1_3, CMD_READ_DIS_4_6, CMD_READ_DIS_7_9, CMD_READ_DIS_10_12, CMD_READ_DIS_13_15, CMD_READ_DIS_16_18 }; const int groupCount = 6; const int perGroup[] = {3, 3, 3, 3, 3, 3}; SPI.beginTransaction(spiSettings); for (int g = 0; g < groupCount; g++) { digitalWrite(CS_PIN, LOW); SPI.transfer16(cmds[g]); uint8_t buffer[8]; for (int i = 0; i < 8; i++) { buffer[i] = SPI.transfer(0x00); } digitalWrite(CS_PIN, HIGH); uint16_t raw[3]; parseReverseFields(buffer, raw, perGroup[g]); for (int i = 0; i < perGroup[g]; i++) { int idx = g * 3 + i; if (idx >= 16) continue; float voltage = (raw[i] - 10240) * 0.0001f; disVoltages[idx] = voltage; } } SPI.endTransaction(); return true; } // ==================== 读取 VREF2 和 V3 ==================== bool readVref2AndV3(float* vref2, float* v3) { uint8_t buffer[8]; SPI.beginTransaction(spiSettings); digitalWrite(CS_PIN, LOW); SPI.transfer16(CMD_READ_VREF2_V3); for (int i = 0; i < 8; i++) { buffer[i] = SPI.transfer(0x00); } digitalWrite(CS_PIN, HIGH); SPI.endTransaction(); uint16_t raw_v3 = (buffer[0] << 8) | buffer[1]; uint16_t raw_vref2 = (buffer[2] << 8) | buffer[3]; *v3 = (raw_v3 - 10240) * 0.0001f; *vref2 = (raw_vref2 - 10240) * 0.0001f; return true; } // ==================== 读取 V5 / DieTemp / VSTK ==================== bool readV5DieTempVStk(float* v5, float* dieTemp, float* vstk) { uint8_t buffer[8]; SPI.beginTransaction(spiSettings); digitalWrite(CS_PIN, LOW); SPI.transfer16(CMD_READ_V5_DIETEMP_VSTK); for (int i = 0; i < 8; i++) { buffer[i] = SPI.transfer(0x00); } digitalWrite(CS_PIN, HIGH); SPI.endTransaction(); uint16_t raw_vstk = (buffer[0] << 8) | buffer[1]; uint16_t raw_dieTemp = (buffer[2] << 8) | buffer[3]; uint16_t raw_v5 = (buffer[4] << 8) | buffer[5]; *vstk = (raw_vstk - 10240) * 0.0016f; *v5 = (raw_v5 - 10240) * 0.0001f; if (raw_dieTemp >= 8000 && raw_dieTemp <= 30000) { *dieTemp = raw_dieTemp * 0.27662 - 3105.59; } else { *dieTemp = NAN; } return true; } // ==================== 更新 BLE 特征值(从 Core 1 调用)==================== void updateBLECharacteristic() { static char jsonBuffer[512]; int offset = 0; float cell[16]; float dieTemp; // 安全拷贝共享数据 portENTER_CRITICAL(&sensorMutex); memcpy(cell, sharedCellVoltages, sizeof(cell)); dieTemp = sharedDieTemp; portEXIT_CRITICAL(&sensorMutex); // 构造 JSON: {"cell":[...], "temp": ...} offset += snprintf(jsonBuffer + offset, sizeof(jsonBuffer) - offset, "{\"cell\":["); for (int i = 0; i < 16; i++) { offset += snprintf(jsonBuffer + offset, sizeof(jsonBuffer) - offset, "%.3f%s", cell[i], (i < 15) ? "," : ""); } if (!isnan(dieTemp)) { offset += snprintf(jsonBuffer + offset, sizeof(jsonBuffer) - offset, "],\"temp\":%.2f}", dieTemp); } else { offset += snprintf(jsonBuffer + offset, sizeof(jsonBuffer) - offset, "],\"temp\":null}"); } // 防溢出兜底 if (offset >= sizeof(jsonBuffer)) { strcpy(jsonBuffer, "{\"err\":\"long\"}"); } // 发送到 BLE if (pCharacteristic && deviceConnected) { pCharacteristic->setValue(jsonBuffer); pCharacteristic->notify(); // 触发通知 } #ifdef DEBUG_GMD1032 Serial.printf(“BLE Data: %s\n”, jsonBuffer); #endif } // ==================== 核心 0:传感器采集任务 ==================== void sensorTask(void *parameter) { for (;😉 { yield(); wakeUpGMD1032(); delay(2); sendCommand(CMD_START_MEASUREMENT); delay(10); float cellVoltages[16] = {0}; float disVoltages[16] = {0}; float vref2 = 0.0f, v3 = 0.0f, v5 = 0.0f, dieTemp = 0.0f, vstk = 0.0f; readAllCellVoltages(cellVoltages); readAllDisVoltages(disVoltages); readVref2AndV3(&vref2, &v3); readV5DieTempVStk(&v5, &dieTemp, &vstk); // 写入共享变量(加锁) portENTER_CRITICAL(&sensorMutex); memcpy(sharedCellVoltages, cellVoltages, sizeof(cellVoltages)); memcpy(sharedDisVoltages, disVoltages, sizeof(disVoltages)); sharedVref2 = vref2; sharedV3 = v3; sharedV5 = v5; sharedDieTemp = dieTemp; sharedVstk = vstk; portEXIT_CRITICAL(&sensorMutex); #ifdef DEBUG_GMD1032 Serial.println(============== GMD1032 全通道数据 ==============); for (int i = 0; i < 16; i++) { if (i % 4 == 0) { Serial.print(" "); } Serial.printf("CELL%2d:%5.3fV ", i + 1, cellVoltages[i]); if ((i + 1) % 4 == 0 || i == 15) { Serial.println(); } } if (!isnan(dieTemp)) { Serial.printf("芯片温度: %.2f°C\n", dieTemp); } else { Serial.println("芯片温度: 无效数据"); } Serial.println("---------------------------------------------"); #endif delay(1000); // 每秒采集一次 } } // ==================== 核心 1:BLE 服务任务 ==================== void bleTask(void parameter) { NimBLEDevice::init(“GMD1032-BLE-Slave”); // 初始化 NimBLEServer pServer = NimBLEDevice::createServer(); // 创建服务器 pServer->setCallbacks(new MyServerCallbacks()); NimBLEService* pService = pServer->createService(SERVICE_UUID); pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY ); pCharacteristic->addDescriptor(new NimBLE2904()); // 添加 CCCD pCharacteristic->setCallbacks(new MyCharacteristicCallbacks()); pCharacteristic->setValue("GMD1032 Ready"); pService->start(); NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising(); pAdvertising->addServiceUUID(NimBLEUUID(SERVICE_UUID)); pAdvertising->start(); Serial.println("BLE 广播已启动,等待连接... (运行于 Core 1)"); for (;;) { if (deviceConnected) { updateBLECharacteristic(); } delay(1000); // 每秒尝试推送一次 } } // ==================== setup() ==================== void setup() { Serial.begin(115200); while (!Serial && millis() < 3000); #ifdef DEBUG_GMD1032 Serial.println(“[DEBUG] GMD1032 调试模式已启用”); #endif // === 初始化 SPI === pinMode(CS_PIN, OUTPUT); digitalWrite(CS_PIN, HIGH); SPI.begin(SCK_PIN, MISO_PIN, MOSI_PIN, CS_PIN); // === 创建两个独立任务,分别绑定到不同核心 === xTaskCreatePinnedToCore(sensorTask, "SensorTask", 4096, NULL, 1, NULL, 0); // Core 0 xTaskCreatePinnedToCore(bleTask, "BleTask", 4096, NULL, 2, NULL, 1); // Core 1 } // ==================== loop() ==================== void loop() { // 所有功能由 FreeRTOS 任务接管,loop 留空 } 上述为从机代码,目前主机输出为 ESP-ROM:esp32s3-20210327 Build:Mar 27 2021 rst:0x1 (POWERON),boot:0x28 (SPI_FAST_FLASH_BOOT) SPIWP:0xee mode:DIO, clock div:1 load:0x3fce3808,len:0x4bc load:0x403c9700,len:0xbd8 load:0x403cc700,len:0x2a0c entry 0x403c98d0 uart1_init… baud_rate=115200,rx= 5,tx= 4 Initializing BLE Master (NimBLE)… 无法找到从机,但手机通过app可以正常连接到从机并接收数据
最新发布
10-12
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值