nkolban / esp32-snippets

Sample ESP32 snippets and code fragments
https://leanpub.com/kolban-ESP32
Apache License 2.0
2.37k stars 710 forks source link

Cannot pair / bond with Android Pixel. Authentication error 0x51. #1134

Closed sanastasiou closed 2 years ago

sanastasiou commented 2 years ago

Hi guys, I am trying to get BLE to work with ESP and I am stuck with this issue. Here's the complete code:

#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLE2902.h>
#ifdef HM_10
#include <SoftwareSerial.h>
#endif

namespace
{
    //globals
    bool     fIsInitialized = false;
    uint8_t  fCounter       = 0U;
    uint16_t fPin           = 0xFFU;
}

namespace ble
{
    /**
     * @brief Callbacks associated with the operation of a %BLE server.
     */
    class ServerCallbacks: public BLEServerCallbacks
    {
        using Base = BLEServerCallbacks;
        uint16_t fConnId = 0xFFFFU;
        esp_ble_gatts_cb_param_t * fConnParams = nullptr;
    public:
        uint16_t getConnId() const
        {
            return fConnId;
        }

        virtual ~ServerCallbacks(){};
        /**
         * @brief Handle a new client connection.
         *
         * When a new client connects, we are invoked.
         *
         * @param [in] pServer A reference to the %BLE server that received the client connection.
         */
        virtual void onConnect(BLEServer *pServer) override
        {
            fConnId = pServer->getConnId();
            Serial.printf("BLE Connected: id: %d, mtu %d\n", fConnId, pServer->getPeerMTU(fConnId));
            Base::onConnect(pServer);
        }
        virtual void onConnect(BLEServer *pServer, esp_ble_gatts_cb_param_t *param) override
        {
            fConnId = pServer->getConnId();
            Serial.printf("BLE Connected: id: %d, mtu %d, param: %d\n", fConnId, pServer->getPeerMTU(fConnId));
            fConnParams = param;
            Base::onConnect(pServer);
        }
        /**
         * @brief Handle an existing client disconnection.
         *
         * When an existing client disconnects, we are invoked.
         *
         * @param [in] pServer A reference to the %BLE server that received the existing client disconnection.
         */
        virtual void onDisconnect(BLEServer *pServer)
        {
            Base::onDisconnect(pServer);
            pServer->getAdvertising()->start();
        }

        /**
         * @brief Handle a new client connection.
         *
         * When the MTU changes this method is invoked.
         *
         * @param [in] pServer A reference to the %BLE server that received the client connection.
         * @param [in] param A reference to esp_ble_gatts_cb_param_t.
         */
        virtual void onMtuChanged(BLEServer *pServer, esp_ble_gatts_cb_param_t *param) override
        {
            Base::onMtuChanged(pServer, param);
        }
    }; // BLEServerCallbacks

    class EncryptionCallbacks : public BLESecurityCallbacks
    {
        uint32_t onPassKeyRequest()
        {
            Serial.printf("PassKeyRequest\n");
            return fPin;
        }
        void onPassKeyNotify(uint32_t pass_key)
        {
            Serial.printf("The passkey Notify number:%d\n", pass_key);
        }
        bool onConfirmPIN(uint32_t pass_key)
        {
            Serial.printf("The passkey YES/NO number:%d\n", pass_key);
            vTaskDelay(5000);
            return true;
        }
        bool onSecurityRequest()
        {
            Serial.printf("SecurityRequest\n");
            return true;
        }

        void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl)
        {
            if (cmpl.success)
            {
                Serial.printf("Starting BLE work!\n");
            }
            else
            {
                Serial.printf("Authentication failed: %d\n", cmpl.fail_reason);
            }
        }
    };

    class ConfigurationCharacteristics : public BLECharacteristicCallbacks
    {
        void onWrite(BLECharacteristic *pCharacteristic)
        {
            std::string value = pCharacteristic->getValue();
            if (value.length() > 0)
            {
                for (int i = 0; i < value.length(); i++)
                    Serial.print(value[i]);
            }
            Serial.println();
        }
    };

    BLECharacteristic * setupService(BLEServer& server, const char * SERVICE_UUID, const char * CHARACTERISTIC_UUID, const uint32_t PROPERTIES)
    {
        BLEService *pService = server.createService(SERVICE_UUID);
        BLECharacteristic *pCharacteristic = pService->createCharacteristic(
            CHARACTERISTIC_UUID,
            PROPERTIES);
            pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
            auto p = new BLE2902();
            p->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
            pCharacteristic->addDescriptor(p);
        return pCharacteristic;
    }

    void setupSecurity(const uint32_t pin)
    {
        BLESecurity *pSecurity = new BLESecurity();
        uint8_t rsp_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
        uint32_t passkey = pin;
        uint8_t auth_option = ESP_BLE_ONLY_ACCEPT_SPECIFIED_AUTH_DISABLE;
        esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &passkey, sizeof(uint32_t));
        pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_BOND);
        pSecurity->setCapability(ESP_IO_CAP_OUT);
        pSecurity->setKeySize(16);
        esp_ble_gap_set_security_param(ESP_BLE_SM_ONLY_ACCEPT_SPECIFIED_SEC_AUTH, &auth_option, sizeof(uint8_t));
        pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK);
        esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &rsp_key, sizeof(uint8_t));
    }
}

namespace constants
{
    int       RX_BLE                            = 12U;                                     // From schematic
    int       TX_BLE                            = 11U;                                     // From schematic
    constexpr unsigned long READ_TIMEOUT        = 2000U;
    constexpr unsigned short INIT_DELAY         = 500U;
    constexpr unsigned long COM_TIMEOUT         = 1000U;
    const     uint8_t MAC_ADDRESS_BYTE_LENGTH   = 6U;
    const     String SERVICE_UUID               = "3B661E69-F877-4CE8-9E89-D8DAB6F25DAA";
    const     String CHARACTERISTIC_UUID        = "82C314B2-E53A-4D5E-B021-FC5887B4373F";
    const     std::__cxx11::string DEVICE_NAME  = "Killswitch";
}

namespace crc
{
    uint16_t crc16(const char* data_p, uint8_t length)
    {
        uint8_t x;
        uint16_t crc = 0xFFFFU;

        while (length--)
        {
            x = crc >> 8U ^ *data_p++;
            x ^= x>>4;
            crc = (crc << 8U) ^ (static_cast<uint16_t>(x << 12U)) ^ (static_cast<uint16_t>(x <<5U)) ^ (static_cast<uint16_t>(x));
        }
        return crc;
    }
}

namespace io
{
    void turnOn(const int pin)
    {
        digitalWrite(pin, HIGH);
    }

    void turnOff(const int pin)
    {
        digitalWrite(pin, LOW);
    }
}

namespace com
{
    bool initBluetooth()
    {
        if (!btStart())
        {
            Serial.println("Failed to initialize controller\n");
            return false;
        }
        if (esp_bluedroid_init() != ESP_OK)
        {
            Serial.println("Failed to initialize bluedroid\n");
            return false;
        }
        if (esp_bluedroid_enable() != ESP_OK)
        {
            Serial.println("Failed to enable bluedroid\n");
            return false;
        }
        return true;
    }

    void printDeviceAddress()
    {
        // Print code here
        const uint8_t* point = esp_bt_dev_get_address();
        char mac_address[12] = {0U};

        for (char i = 0; i < constants::MAC_ADDRESS_BYTE_LENGTH; i++)
        {
            char str[3];
            sprintf(str, "%02X", (int)point[i]);
            mac_address[(i*2)]   = str[0];
            mac_address[(i*2)+1] = str[1];
            Serial.print(str);
            if (i < 5)
            {
                Serial.print(":");
            }
        }
        Serial.print("\n");
        fPin = crc::crc16(mac_address, sizeof(mac_address));
        Serial.print("Device BLE Pin: "); Serial.print(fPin); Serial.print("\n");
    }
}

void initializeBluetoothModule()
{
    //generate pin
    while (!com::initBluetooth());
    com::printDeviceAddress();
    //setup server
    BLEDevice::init(constants::DEVICE_NAME);
    BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT);
    BLEDevice::setSecurityCallbacks(new ble::EncryptionCallbacks());
    BLEServer *pServer = BLEDevice::createServer();
    pServer->setCallbacks(new ble::ServerCallbacks());
    auto pCharacteristic = ble::setupService(*pServer, constants::SERVICE_UUID.c_str(), constants::CHARACTERISTIC_UUID.c_str(), BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE);

    pCharacteristic->setValue("Hello World");
    pServer->getServiceByUUID(constants::SERVICE_UUID.c_str())->start();
    pServer->getAdvertising()->start();

    ble::setupSecurity(fPin);
    Serial.println("Characteristic defined! Now you can read it in your phone!");
}

// the setup function runs once when you press reset or power the board
void setup()
{
    // Open console Serial port
    Serial.begin(115200);
    while (!Serial);
    Serial.printf("Sketch:   %s\n", __FILE__);
    Serial.print(F("Uploaded: ")); Serial.println(__DATE__);
    Serial.println(F("Started"));
    //give some time for serial coms to establish
    delay(constants::INIT_DELAY);
    initializeBluetoothModule();
    Serial.println("Bluetooth initialization done...\n");
}

// the loop function runs over and over again forever
void loop()
{
    vTaskDelay(10);
}

On the opposite side, an iPhone X SE works perfectly with this exact same code. I have tried using several google "fixes" but without success.

Also here's the log of the code above:

Device BLE Pin: 60927
Characteristic defined! Now you can read it in your phone!
Bluetooth initialization done...

BLE Connected: id: 0, mtu 23
BLE Connected: id: 0, mtu 23, param: 1073490300
The passkey Notify number:60927
Authentication failed: 81

I would be grateful for any help! Thanks!

sanastasiou commented 2 years ago

Fixed with this change:

pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_MITM_BOND);