Encore une fois sur les capteurs BLE, de température et Xiaomi

Il n'y a pas si longtemps, j'ai réussi à obtenir les célèbres capteurs de température et d'humidité Xiaomi. Ces capteurs ont à juste titre gagné en popularité, car à leur prix assez bas, ils sont assez pratiques à utiliser et ils savent également comment transmettre leurs lectures via le protocole BLE au même Mi Home. De plus, Internet tout entier regorge d'options pour connecter ces capteurs à Home Assistant , MajorDoMo et à d'autres systèmes.


Mais cela ne me semblait pas suffisant et je voulais tout faire à ma façon (ne me demandez pas pourquoi et pourquoi, je voulais juste). À savoir, je voulais lire les données de capteurs qui sont accrochés à la maison et qui sont en quelque sorte intéressants à travailler avec eux. Par conséquent, j'ai fouillé dans mes bacs électroniques et y ai trouvé le module ESP32.



Un rapide google a montré: ESP32 est ce dont j'ai besoin. Il sait comment Bluetooth et WiFi, est programmé à partir de l'IDE Arduino et me permettra d'obtenir des lectures du capteur et de les envoyer via WiFi partout où j'ai besoin (au moins vers le serveur domestique, même vers le cloud). De plus, un tutoriel très rapide et simple a été trouvé qui vient de résoudre mon problème. Mais il s'est avéré que tout n'est pas si simple ...


Premiers problèmes


, . … , .


, 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