Lagi tentang BLE, sensor suhu dan Xiaomi

Belum lama ini, saya berhasil mendapatkan sensor suhu dan kelembaban Xiaomi yang terkenal. Sensor-sensor ini sepatutnya mendapatkan popularitas yang luas, karena dengan harga yang cukup rendah, mereka cukup nyaman untuk digunakan, dan mereka juga tahu bagaimana mengirimkan bacaan mereka melalui protokol BLE ke Mi Home yang sama. Selain itu, seluruh Internet dipenuhi dengan pilihan untuk menghubungkan sensor-sensor ini ke Home Assistant , MajorDoMo dan sistem lainnya.


Tapi menurut saya itu tidak cukup dan saya ingin melakukan semuanya dengan cara saya sendiri (jangan tanya kenapa dan kenapa, saya hanya mau). Yaitu, saya ingin membaca data dari sensor yang tergantung di sekitar rumah dan entah bagaimana menarik untuk bekerja dengannya. Oleh karena itu, saya mencari-cari di tempat sampah elektronik saya dan menemukan modul ESP32 di sana.



Google cepat menunjukkan: ESP32 adalah yang saya butuhkan. Ia tahu cara Bluetooth dan WiFi, diprogram dari Arduino IDE dan akan memungkinkan saya untuk mendapatkan pembacaan dari sensor dan mengirimkannya melalui WiFi di mana pun saya butuhkan (setidaknya ke server rumah, bahkan ke cloud). Selain itu, ditemukan tutorial yang sangat cepat dan sederhana yang baru saja menyelesaikan masalah saya. Tapi ternyata, tidak semuanya begitu sederhana ...


Masalah pertama


, . … , .


, ESP32, . ( , ) . , . , BLE . , , - .



BLE 2- . (discover mode) (connection mode). , Bluetooth . . - . , .


Xiaomi , . . . , .


- ?


. , (advertising ).


void initBluetooth()
{
    BLEDevice::init("");
    pBLEScan = BLEDevice::getScan(); //create new scan
    pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
    pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
    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) {
        //      [ ][ ][]
        //      0x16,     0x95 0xfe
        //   ,       
        //         
        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);

        // 11      
        // 0x0D -   
        // 0x0A - 
        // 0x06 - 
        // 0x04 - 

        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:


All Articles