ESP32C3与Sylvania A19智能灯泡BLE连接及写入失败求助
问题分析与解决方案
一、连接失败的核心问题及修复
你的代码存在几个关键的连接逻辑错误,同时服务UUID的获取方式也不准确:
扫描回调内直接连接的资源冲突
扫描回调onResult会被频繁触发,在回调内创建BLEClient并发起连接会导致资源竞争,且扫描未停止时连接稳定性极差。应该在找到目标设备后立即停止扫描,再在主线程中执行连接操作。缺少连接与服务获取的错误检查
原代码没有判断connect()是否成功,也没有检查getService()返回的服务是否为空,一旦UUID错误或连接失败,后续代码会直接崩溃。依赖广播UUID的局限性
BLE广播只会携带部分服务UUID,灯泡的完整服务列表需要连接后主动发现,不能直接用广播的getServiceUUID()作为控制服务的UUID。
二、写入无效的问题根源
写入值格式错误
writeValue("00 00 00 00 00 00")传递的是带空格的字符串,而BLE特征写入需要的是字节数组,这种字符串会被当作ASCII字符发送,并非你预期的6个0x00字节。指令格式不匹配
Sylvania智能灯泡的BLE控制通常需要符合其私有协议,并非简单的00串或字符串指令,需要找到正确的可写特征后,再匹配对应的指令格式。
三、修改后的代码
#include <BLEDevice.h> #include <BLEUtils.h> #include <BLEScan.h> #include <BLEAdvertisedDevice.h> #include <BLEClient.h> #include <BLEAddress.h> #define BLE_SCAN_DURATION 5 #define SYLVANIA_SMART_BULB_ADDRESS "你的灯泡MAC地址" BLEAddress targetBulbAddr(SYLVANIA_SMART_BULB_ADDRESS); bool bulbFound = false; class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks { public: void onResult(BLEAdvertisedDevice advertisedDevice) { Serial.printf("发现设备: %s | 广播名称: %s\n", advertisedDevice.getAddress().toString().c_str(), advertisedDevice.getName().c_str()); // 匹配目标设备 if (advertisedDevice.getAddress() == targetBulbAddr) { Serial.println("找到目标Sylvania灯泡,停止扫描"); bulbFound = true; BLEDevice::getScan()->stop(); // 立即停止扫描 } } }; // 连接设备并发现所有服务与特征 void connectAndDiscoverServices() { BLEClient* pClient = BLEDevice::createClient(); if (!pClient) { Serial.println("创建BLE客户端失败"); return; } // 发起连接并检查结果 if (!pClient->connect(targetBulbAddr)) { Serial.println("连接灯泡失败"); pClient->deleteClient(); return; } Serial.println("成功连接灯泡"); // 发现所有服务 std::vector<BLERemoteService*> services = pClient->getServices(); Serial.printf("共发现 %d 个服务:\n", services.size()); for (auto service : services) { Serial.printf("服务UUID: %s\n", service->getUUID().toString().c_str()); // 发现该服务下的所有特征 std::vector<BLERemoteCharacteristic*> chars = service->getCharacteristics(); for (auto chr : chars) { // 打印特征属性(可写、可读、通知等) uint32_t props = chr->getProperties(); String propStr = ""; if (props & BLERemoteCharacteristic::PROPERTY_WRITE) propStr += "可写 "; if (props & BLERemoteCharacteristic::PROPERTY_READ) propStr += "可读 "; if (props & BLERemoteCharacteristic::PROPERTY_NOTIFY) propStr += "通知 "; Serial.printf(" 特征UUID: %s | 属性: %s\n", chr->getUUID().toString().c_str(), propStr.c_str()); } } // 假设你找到正确的可写特征UUID后,替换下面的内容 // BLERemoteService* pService = pClient->getService("正确的服务UUID"); // if (pService) { // BLERemoteCharacteristic* pChar = pService->getCharacteristic("正确的特征UUID"); // if (pChar && pChar->canWrite()) { // // 发送正确的字节指令,示例:6个0x00字节 // uint8_t cmd[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // pChar->writeValue(cmd, sizeof(cmd)); // Serial.println("已发送控制指令"); // } // } pClient->disconnect(); pClient->deleteClient(); } void setup() { Serial.begin(115200); BLEDevice::init("ESP32C3-BLE-Client"); // 启动扫描 BLEScan* pScan = BLEDevice::getScan(); pScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); pScan->setActiveScan(true); pScan->start(BLE_SCAN_DURATION, false); // 第二个参数false表示扫描结束后不自动删除扫描对象 // 扫描结束后检查是否找到目标设备 if (bulbFound) { connectAndDiscoverServices(); } else { Serial.println("扫描未找到目标灯泡"); } } void loop() { // 如需重复扫描控制,可在此添加逻辑 delay(5000); }
四、后续操作步骤
- 替换代码中的
SYLVANIA_SMART_BULB_ADDRESS为你实际的灯泡MAC地址(格式:AA:BB:CC:DD:EE:FF)。 - 上传代码后,打开串口监视器,查看打印的服务与特征列表,找到你手机APP中看到的2个可写特征,记录它们的UUID。
- 取消代码中注释的控制逻辑,替换为正确的服务UUID和特征UUID,并根据灯泡的协议调整控制指令(可尝试查找Sylvania BLE控制的公开协议,或通过手机APP抓包分析指令)。
内容的提问来源于stack exchange,提问作者Tyler Yarborough
相关产品推荐
相关产品推荐

