本帖最后由 lhy741059930 于 2025-8-21 22:43 编辑
OK,这是我第N次折腾极米的投影仪(接入Homekit),记录一下
硬件:苹果手机1台,安卓手机一台,ESP32 S3 zero (之前用剩下的,如果是买的话,应该ESP32 C3 更加便宜)吐槽一下,这玩意儿真不耐玩。 不知道怎么就不能通过串口读取数据,反复验证应该是坏了,不过功能正常。
软件(APP):Si Connect(苹果),EFR Connect(安卓)其实俩软件应该是同一个东西。一个手机应该就能搞定,我以前是用两个手机搞得。这次直接用了。 Arduino 就不用说了,必须要的。
1. 首先就是打开苹果上的Si Connect 然后搜索,同时不停按极米遥控器的开关。会扫描到下面的这个广播信息。在有数据的时候赶紧截屏。
2.接着打开安卓的EFR Connect 到Configure页面 新增一个广播,并如图设置参数。当然参数应该是你用Si Connect获取到的,而不是直接抄我这个(当然如果你是M1,也许可以),新增打开广播开关,就应该可以看到投影机开机了。
3.接下来,打开你的Arduino创建一个新项目,把代码放进去,根据你的wifi和mqtt修改一下参数,并选择好开发板,烧录进去。
#include "BLEDevice.h"
#include "BLEAdvertising.h"
#include "BLEServer.h"
#include <WiFi.h>
#include <PubSubClient.h>
BLEAdvertising* pAdvertising;
BLEServer* pServer;
// WiFi和MQTT配置
const char* ssid = ""; // 请替换为你的WiFi名称
const char* password = "."; // 请替换为你的WiFi密码
const char* mqtt_server = ""; // 请替换为你的MQTT服务器地址
const char* mqtt_username = ""; // MQTT用户名
const char* mqtt_password = ""; // MQTT密码
const char* mqtt_topic = "XGIMI/Power/set"; // MQTT主题
WiFiClient espClient;
PubSubClient client(espClient);
// 蓝牙控制变量
bool bleInitialized = false;
int broadcastCount = 0;
const int maxBroadcasts = 5;
const int broadcastInterval = 500; // 500ms间隔
unsigned long lastBroadcastTime = 0;
void setup() {
Serial.begin(115200);
delay(1000);
// 连接WiFi
setupWiFi();
// 设置MQTT
client.setServer(mqtt_server, 1883);
client.setCallback(mqttCallback);
Serial.println("系统初始化完成,等待MQTT消息...");
}
void setupAdvertising() {
// 设置广播功率 (使用可用的功率等级)
BLEDevice::setPower(ESP_PWR_LVL_N9); // 使用-9dBm,最接近-8dBm
// 设置设备名称
pAdvertising->setAppearance(0x03C0); // HID设备外观
// 添加服务UUID 0x1812 (Human Interface Device)
BLEUUID serviceUUID("1812");
pAdvertising->addServiceUUID(serviceUUID);
// 制造商数据内容 (从图片中的十六进制数据)
uint8_t mfgData[] = {
0x46, 0x00, // Company Code (Little Endian): 0x0046
0x47, 0x80, 0x17, 0xB2, 0xC1, 0x84, 0x18, 0xFF,
0xFF, 0xFF, 0x30, 0x43, 0x52, 0x4B, 0x54, 0x4D
};
// 转换为Arduino String类型
String manufacturerData = "";
for (int i = 0; i < sizeof(mfgData); i++) {
manufacturerData += (char)mfgData[i];
}
// 设置制造商数据
BLEAdvertisementData advData;
advData.setManufacturerData(manufacturerData);
advData.setName("New adv");
advData.setCompleteServices(serviceUUID);
advData.setFlags(0x06); // General Discoverable + BR/EDR Not Supported
pAdvertising->setAdvertisementData(advData);
// 设置广播参数 - 针对ESP32-S3优化
pAdvertising->setMinInterval(160); // 100ms (160 * 0.625ms)
pAdvertising->setMaxInterval(240); // 150ms
pAdvertising->setScanResponse(true);
}
void loop() {
// 保持MQTT连接
if (!client.connected()) {
reconnectMQTT();
}
client.loop();
// 处理蓝牙广播
handleBLEBroadcast();
delay(100); // 短暂延时
}
// WiFi连接设置
void setupWiFi() {
WiFi.begin(ssid, password);
Serial.print("连接WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.println("WiFi连接成功!");
Serial.print("IP地址: ");
Serial.println(WiFi.localIP());
}
// MQTT重连
void reconnectMQTT() {
while (!client.connected()) {
Serial.print("尝试MQTT连接...");
if (client.connect("ESP32Client", mqtt_username, mqtt_password)) {
Serial.println("MQTT连接成功!");
// 订阅主题
client.subscribe(mqtt_topic);
Serial.println("已订阅主题: " + String(mqtt_topic));
} else {
Serial.print("连接失败, rc=");
Serial.print(client.state());
Serial.println(" 5秒后重试");
delay(5000);
}
}
}
// MQTT消息回调
void mqttCallback(char* topic, byte* payload, unsigned int length) {
String message = "";
for (int i = 0; i < length; i++) {
message += (char)payload[i];
}
Serial.println("收到MQTT消息: [" + String(topic) + "] " + message);
// 检查是否为"open"消息
if (message == "open") {
Serial.println("收到开启指令,准备启动蓝牙广播...");
startBLEBroadcastSequence();
}
}
// 启动蓝牙广播序列
void startBLEBroadcastSequence() {
if (!bleInitialized) {
Serial.println("初始化蓝牙设备...");
// 初始化BLE设备
BLEDevice::init("New adv");
// 创建BLE服务器
pServer = BLEDevice::createServer();
// 获取广播对象
pAdvertising = BLEDevice::getAdvertising();
// 设置广播参数
setupAdvertising();
bleInitialized = true;
Serial.println("蓝牙初始化完成");
}
// 重置广播计数
broadcastCount = 0;
lastBroadcastTime = millis();
// 开始第一次广播
BLEDevice::startAdvertising();
broadcastCount = 1;
Serial.println("开始蓝牙广播 (" + String(broadcastCount) + "/" + String(maxBroadcasts) + ")");
}
// 处理蓝牙广播逻辑
void handleBLEBroadcast() {
if (broadcastCount > 0 && broadcastCount < maxBroadcasts) {
if (millis() - lastBroadcastTime >= broadcastInterval) {
// 停止当前广播
BLEDevice::getAdvertising()->stop();
delay(50); // 短暂延时
// 开始下一次广播
BLEDevice::startAdvertising();
broadcastCount++;
lastBroadcastTime = millis();
Serial.println("蓝牙广播 (" + String(broadcastCount) + "/" + String(maxBroadcasts) + ")");
}
} else if (broadcastCount >= maxBroadcasts) {
// 完成所有广播,关闭蓝牙
if (bleInitialized) {
Serial.println("广播完成,关闭蓝牙以节省功耗...");
BLEDevice::getAdvertising()->stop();
BLEDevice::deinit();
bleInitialized = false;
broadcastCount = 0;
Serial.println("蓝牙已关闭,系统进入低功耗模式");
}
}
}
[size=0.8em]
|