منذ وقت ليس ببعيد ، تمكنت من الحصول على أجهزة استشعار درجة الحرارة والرطوبة الشهيرة من Xiaomi. لقد اكتسبت هذه المستشعرات استحسانًا واسعًا ، لأن سعرها منخفض إلى حد ما ، فهي مريحة تمامًا للاستخدام ، كما أنها تعرف كيفية نقل قراءاتها عبر بروتوكول BLE إلى Mi Home نفسه. بالإضافة إلى ذلك ، فإن الإنترنت بالكامل مليء بالخيارات لتوصيل هذه المستشعرات بـ Home Assistant و MajorDoMo والأنظمة الأخرى.
ولكن يبدو لي أنه ليس كافيًا وأردت أن أفعل كل شيء بطريقتنا الخاصة (لا تسألني لماذا ولماذا ، أردت فقط). وبالتحديد ، أردت قراءة البيانات من أجهزة الاستشعار المعلقة حول المنزل ومثيرة للاهتمام بطريقة ما للعمل معها. لذلك ، قمت بالتفتيش في صناديقي الإلكترونية ووجدت وحدة ESP32 هناك.

أظهر جوجل سريع: ESP32 هو ما أحتاجه. إنه يعرف كيفية البلوتوث والواي فاي ، وهو مبرمج من Arduino IDE وسيسمح لي بالحصول على قراءات من المستشعر وإرسالها عبر WiFi أينما احتجت (على الأقل إلى الخادم المنزلي ، حتى إلى السحابة). بالإضافة إلى ذلك ، تم العثور على برنامج تعليمي سريع وبسيط جدًا حل مشكلتي للتو. ولكن كما اتضح ، ليس كل شيء بهذه البساطة ...
المشاكل الأولى
, . … , .
, ESP32, . ( , ) . , . , BLE . , , - .
BLE 2- . (discover mode) (connection mode). , Bluetooth . . - . , .
Xiaomi , . . . , .
- ?
. , (advertising ).
void initBluetooth()
{
BLEDevice::init("");
pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true);
pBLEScan->setInterval(0x50);
pBLEScan->setWindow(0x30);
}
:
void onResult(BLEAdvertisedDevice advertisedDevice)
{
if (advertisedDevice.haveName() && advertisedDevice.haveServiceData() && !advertisedDevice.getName().compare("MJ_HT_V1")) {
std::string strServiceData = advertisedDevice.getServiceData();
uint8_t cServiceData[100];
char charServiceData[100];
strServiceData.copy((char *)cServiceData, strServiceData.length(), 0);
Serial.printf("\n\nAdvertised Device: %s\n", advertisedDevice.toString().c_str());
for (int i=0;i<strServiceData.length();i++) {
sprintf(&charServiceData[i*2], "%02x", cServiceData[i]);
}
std::stringstream ss;
ss << "fe95" << charServiceData;
Serial.print("Payload:");
Serial.println(ss.str().c_str());
char eventLog[256];
unsigned long value, value2;
char charValue[5] = {0,};
switch (cServiceData[11]) {
case 0x04:
sprintf(charValue, "%02X%02X", cServiceData[15], cServiceData[14]);
value = strtol(charValue, 0, 16);
if(METRIC)
{
current_temperature = (float)value/10;
}else
{
current_temperature = CelciusToFahrenheit((float)value/10);
}
displayTemperature();
break;
case 0x06:
sprintf(charValue, "%02X%02X", cServiceData[15], cServiceData[14]);
value = strtol(charValue, 0, 16);
current_humidity = (float)value/10;
displayHumidity();
Serial.printf("HUMIDITY_EVENT: %s, %d\n", charValue, value);
break;
case 0x0A:
sprintf(charValue, "%02X", cServiceData[14]);
value = strtol(charValue, 0, 16);
Serial.printf("BATTERY_EVENT: %s, %d\n", charValue, value);
break;
case 0x0D:
sprintf(charValue, "%02X%02X", cServiceData[15], cServiceData[14]);
value = strtol(charValue, 0, 16);
if(METRIC)
{
current_temperature = (float)value/10;
}else
{
current_temperature = CelciusToFahrenheit((float)value/10);
}
displayTemperature();
Serial.printf("TEMPERATURE_EVENT: %s, %d\n", charValue, value);
sprintf(charValue, "%02X%02X", cServiceData[17], cServiceData[16]);
value2 = strtol(charValue, 0, 16);
current_humidity = (float)value2/10;
displayHumidity();
Serial.printf("HUMIDITY_EVENT: %s, %d\n", charValue, value2);
break;
}
}
}
, - .
switch, 11 service data . , 11 . .
advertising (payload). , , . . payload ( ):
020106121695fe5020aa01ab9f0231342d580a10014309094d4a5f48545f563105030f180a180916ffffc8b33f8a48db
. ( 0x02) . , ( ). . ( ) .
0x16, service data, .. , . 2:
121695fe5020aa01ab9f0231342d580a100143
0916ffffc8b33f8a48db
, , 11 , , switch (0x0A). , , . . , , .
?
- , , , . ESP32 Arduino. , , getServiceData , . .. , payload service data. , . , ( 1.0.4). Arduino IDE ESP32 Boards Manager . getServiceData() service data. , . , .
. . , , . , payload service data ( findServiceData).
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
uint8_t* findServiceData(uint8_t* data, size_t length, uint8_t* foundBlockLength) {
uint8_t* rightBorder = data + length;
while (data < rightBorder) {
uint8_t blockLength = *data;
if (blockLength < 5) {
data += (blockLength+1);
continue;
}
uint8_t blockType = *(data+1);
uint16_t serviceType = *(uint16_t*)(data + 2);
if (blockType == 0x16 && serviceType == 0xfe95) {
*foundBlockLength = blockLength-3;
return data+4;
}
data += (blockLength+1);
}
return nullptr;
}
void onResult(BLEAdvertisedDevice advertisedDevice) {
if (!advertisedDevice.haveName() || advertisedDevice.getName().compare("MJ_HT_V1"))
return;
uint8_t* payload = advertisedDevice.getPayload();
size_t payloadLength = advertisedDevice.getPayloadLength();
Serial.printf("\n\nAdvertised Device: %s\n", advertisedDevice.toString().c_str());
printBuffer(payload, payloadLength);
uint8_t serviceDataLength=0;
uint8_t* serviceData = findServiceData(payload, payloadLength, &serviceDataLength);
if (serviceData == nullptr) {
return;
}
Serial.printf("Found service data len: %d\n", serviceDataLength);
printBuffer(serviceData, serviceDataLength);
switch (serviceData[11])
{
case 0x0D:
{
float temp = *(uint16_t*)(serviceData + 11 + 3) / 10.0;
float humidity = *(uint16_t*)(serviceData + 11 + 5) / 10.0;
Serial.printf("Temp: %f Humidity: %f\n", temp, humidity);
}
break;
case 0x04:
{
float temp = *(uint16_t*)(serviceData + 11 + 3) / 10.0;
Serial.printf("Temp: %f\n", temp);
}
break;
case 0x06:
{
float humidity = *(uint16_t*)(serviceData + 11 + 3) / 10.0;
Serial.printf("Humidity: %f\n", humidity);
}
break;
case 0x0A:
{
int battery = *(serviceData + 11 + 3);
Serial.printf("Battery: %d\n", battery);
}
break;
default:
break;
}
}
};
, . - ESP32 StackOverflow, . , . , -, , , . , .
- , , - . , , , .
.
UPD: