espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.51k stars 7.26k forks source link

esp_ble_tx_power_set - Documentation unclear (IDFGH-363) #2390

Closed Maldus512 closed 5 years ago

Maldus512 commented 6 years ago

Environment 4.17.14-arch1-1-ARCH x86_64 GNU/Linux

Problem Description

How do I increase the transmission/advertisement range of the ESP32 as BLE device beyond 10 meters? I'm trying to use the ESP32 as a GATT server communicating with a smartphone. The datasheet states (https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf , section 1.3) that the esp32 should be able to transmit as class-1 device without external power amplifier, which means around 100m range. I am however unable to achieve this.

The documentation is somewhat lacking, the only references I could find being:

  1. the PHY settings in the menuconfig, already set at the highest value (20)
  2. the esp_ble_tx_power_set function (https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/bluetooth/controller_vhci.html?highlight=esp_power_level_t?highlight=esp_power_level_t), that allows me to set the transmit power to 9dbm max (class-1 should be 20dbm). In practice however it doesn't work for me, as it disappears at ~10m.

I am simply adding the function call on the connection established event (as described in the docs) in the gatt_server example. My only addition is here:

 case ESP_GATTS_CONNECT_EVT: {

    esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, ESP_PWR_LVL_P9);
        esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_CONN_HDL0, ESP_PWR_LVL_P9);
        esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_CONN_HDL1, ESP_PWR_LVL_P9);
        esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_CONN_HDL2, ESP_PWR_LVL_P9);
        esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_CONN_HDL3, ESP_PWR_LVL_P9);
        esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_CONN_HDL4, ESP_PWR_LVL_P9);
        esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_CONN_HDL5, ESP_PWR_LVL_P9);
        esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_CONN_HDL6, ESP_PWR_LVL_P9);
        esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_CONN_HDL8, ESP_PWR_LVL_P9);
        esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, ESP_PWR_LVL_P9);

        esp_ble_conn_update_params_t conn_params = {0};
        memcpy(conn_params.bda, param->connect.remote_bda, sizeof(esp_bd_addr_t));
        /* For the IOS system, please reference the apple official documents about the ble connection parameters restrictions. */
        conn_params.latency = 0;
        conn_params.max_int = 0x20;    // max_int = 0x20*1.25ms = 40ms
        conn_params.min_int = 0x10;    // min_int = 0x10*1.25ms = 20ms
        conn_params.timeout = 400;    // timeout = 400*10ms = 4000ms
        ESP_LOGI(GATTS_TAG, "ESP_GATTS_CONNECT_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x:",
                 param->connect.conn_id,
                 param->connect.remote_bda[0], param->connect.remote_bda[1], param->connect.remote_bda[2],
                 param->connect.remote_bda[3], param->connect.remote_bda[4], param->connect.remote_bda[5]);
        gl_profile_tab[PROFILE_A_APP_ID].conn_id = param->connect.conn_id;
        //start sent the update connection parameters to the peer device.
        esp_ble_gap_update_conn_params(&conn_params);
        break;
    }

I've tried every possible value power_type argument since I couldn't understand what a "connection handle" is.

This task should be simple enough, can anyone point me in the right direction? Is it possible at all to transmit further than 10 meters?

chegewara commented 6 years ago

I just did test to answer your question. My rig is:

Maybe its problem with antenna on your esp32 board or with smartphone.

Maldus512 commented 6 years ago

Many thanks for taking the time to test and answer me.

I will now try a different configuration and see if anything changes. Just to be clear, are you calling esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, ESP_PWR_LVL_P9); right at the beginning, along with the bt stack initialization?

chegewara commented 6 years ago

After ble init but before start advertising.

Maldus512 commented 6 years ago

I can confirm it works; I was able to scan and connect to my ESP32 from a ~30m range (I can't get further away while keeping a clear path).

I was probably having issues with obstacles. Initially I was trying it indoors, but apparently even a corridor corner is enough to severely cripple the signal strength.

The documentation problem however still holds: esp_ble_tx_power_set is described as

Set BLE TX power Connection Tx power should only be set after connection created. 

I am still confused about the "only be set after connection created" bit. What does it mean? I set the tx power right after esp_bluedroid_init and it seems to work.

thomasmeon commented 6 years ago

Hello @chegewara, I have a similar problem to @Maldus512 one. I try to connect an esp32 (as a server) and another esp32 (as a client). I use the "BLE_client" and "BLE_server" examples. My problem is that I can make the connection between them only when they are close ( less than 1 meter ). Otherwise, when I try to put them further (like 2 meters or more), the connection does not work.. It's like my TxPower is not enough to make the connection.

I tried to add : "esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, ESP_PWR_LVL_P9);" right after the ble init and before start advertising as you said before (on my server code) but still, the problem is the same. Do you have an idea what could be wrong with that ?

My esp32 model is WROOM-32.

My code ( client ) is : `

#include "BLEDevice.h"
//#include "BLEScan.h"

// The remote service we wish to connect to.
static BLEUUID serviceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59");
// The characteristic of the remote service we are interested in.
static BLEUUID    charUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8");

static BLEAddress *pServerAddress;
static boolean doConnect = false;
static boolean connected = false;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static BLERemoteService* test;

static void notifyCallback(
  BLERemoteCharacteristic* pBLERemoteCharacteristic,
  uint8_t* pData,
  size_t length,
  bool isNotify) {
    Serial.print("Notify callback for characteristic ");
    Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
    Serial.print(" of data length ");
    Serial.println(length);
}

bool connectToServer(BLEAddress pAddress) {
    Serial.print("Forming a connection to ");
    Serial.println(pAddress.toString().c_str());

    BLEClient*  pClient  = BLEDevice::createClient();
    Serial.println(" - Created client");

    // Connect to the remove BLE Server.

static boolean temp = false;
    temp = pClient->connect(pAddress);
    if (temp) {
    Serial.println(" - Connected to server");
    }

    // Obtain a reference to the service we are after in the remote BLE server.
    BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
    if (pRemoteService == nullptr) {
      Serial.print("Failed to find our service UUID: ");
      Serial.println(serviceUUID.toString().c_str());
      return false;
    }
    Serial.println(" - Found our service");

    // Obtain a reference to the characteristic in the service of the remote BLE server.
    pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
    if (pRemoteCharacteristic == nullptr) {
      Serial.print("Failed to find our characteristic UUID: ");
      Serial.println(charUUID.toString().c_str());
      return false;
    }
    Serial.println(" - Found our characteristic");

    // Read the value of the characteristic.
    std::string value = pRemoteCharacteristic->readValue();
    Serial.print("The characteristic value was: ");
    Serial.println(value.c_str());

    pRemoteCharacteristic->registerForNotify(notifyCallback);
}
/**
 * Scan for BLE servers and find the first one that advertises the service we are looking for.
 */
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
 /**
   * Called for each advertising BLE server.
   */
  void onResult(BLEAdvertisedDevice advertisedDevice) {
    Serial.print("BLE Advertised Device found: **");
    Serial.print(advertisedDevice.toString().c_str());
    Serial.println("**");

    // We have found a device, let us now see if it contains the service we are looking for.
    if (advertisedDevice.haveServiceUUID() && advertisedDevice.getServiceUUID().equals(serviceUUID)) {

      // 
      Serial.print("Found our device!  address: "); 
      advertisedDevice.getScan()->stop();
      delay(2000);
      pServerAddress = new BLEAddress(advertisedDevice.getAddress());
      Serial.print("OK1 ");
      doConnect = true;
      Serial.print("OK2 ");
    } // Found our server
  } // onResult
}; // MyAdvertisedDeviceCallbacks

void setup() {
  Serial.begin(115200);
  Serial.println("Starting Arduino BLE Client application...");
  BLEDevice::init("");

  // Retrieve a Scanner and set the callback we want to use to be informed when we
  // have detected a new device.  Specify that we want active scanning and start the
  // scan to run for 30 seconds.
  BLEScan* pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->setActiveScan(true);
  pBLEScan->start(45);
} // End of setup.

// This is the Arduino main loop function.
void loop() {

  // If the flag "doConnect" is true then we have scanned for and found the desired
  // BLE Server with which we wish to connect.  Now we connect to it.  Once we are 
  // connected we set the connected flag to be true.
  if (doConnect == true) {
    //Serial.print("OK3 ");
    if (connectToServer(*pServerAddress)) {
      //Serial.print("OK4 ");
      Serial.println("We are now connected to the BLE Server.");
      connected = true;
    } else {
      Serial.println("We have failed to connect to the server; there is nothin more we will do.");
    }
    doConnect = false;
  }

  // If we are connected to a peer BLE Server, update the characteristic each time we are reached
  // with the current time since boot.
  if (connected) {
    Serial.println("///////////CONNETED//////////(test)");

   //Serial.println ((pRemoteCharacteristic->toString()).c_str());
    //String newValue = "Time since boot: " + String(millis()/1000);
    //Serial.println("Setting new characteristic value to \"" + newValue + "\"");

    // Set the characteristic's value to be the array of bytes that is actually a string.
    //pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length());

  }
  delay(1000); // Delay a second between loops.
} // End of the loop

and my server code on the other esp32 :


#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>

// See the following for generating UUIDs:
// https://www.uuidgenerator.net/

#define SERVICE_UUID        "91bad492-b950-4226-aa2b-4ede9fa42f59"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"

void setup() {
  Serial.begin(115200);
  Serial.println("Starting BLE work!");
  Serial.println(ESP_BLE_PWR_TYPE_ADV);
  Serial.println(ESP_PWR_LVL_P7);

  BLEDevice::init("TOM83");
  esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, ESP_PWR_LVL_P9);
  esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_CONN_HDL0, ESP_PWR_LVL_P9);
  esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_CONN_HDL1, ESP_PWR_LVL_P9);
  esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_CONN_HDL2, ESP_PWR_LVL_P9);
  esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_CONN_HDL3, ESP_PWR_LVL_P9);
  esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_CONN_HDL4, ESP_PWR_LVL_P9);
  esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_CONN_HDL5, ESP_PWR_LVL_P9);
  esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_CONN_HDL6, ESP_PWR_LVL_P9);
  esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_CONN_HDL8, ESP_PWR_LVL_P9);
  esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, ESP_PWR_LVL_P9);
  BLEServer *pServer = BLEDevice::createServer();
  BLEService *pService = pServer->createService(SERVICE_UUID);
  BLECharacteristic *pCharacteristic = pService->createCharacteristic(
                                         CHARACTERISTIC_UUID,
                                         BLECharacteristic::PROPERTY_READ |
                                         BLECharacteristic::PROPERTY_WRITE
                                       );

  pCharacteristic->setValue("Hello World says Neil");
  pService->start();

  BLEAdvertising *pAdvertising = pServer->getAdvertising();
  pAdvertising->addServiceUUID(pService->getUUID());
  pAdvertising->start();
  Serial.println("Characteristic defined! Now you can read it in your phone!");
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(2000);
}

Thanks in advance

Thomas

chegewara commented 6 years ago

@thomasmeon first try to upload server code to both esp32 and see with nRF connect or any other tool if they both are advertising with the same RSSI from the same distance. If all is ok then try to upload server and client to esp32 and if still is not working as you expecting try to switch code and flash server and client apps to the other esp32.

As you can see all is working for me and @Maldus512 , its issue with obstacles but i am assuming you dont have any obstacles if you are testing in range 1 m.

thomasmeon commented 6 years ago

@chegewara Hey, I just did what you said and I finally found out what was wrong. Actually i plugged a DHT11 sensor on the same breadboard, and I used the G15 pin for the data transmission, and the problem was about this pin. This pin can have a lot of different functionalities , so I just plugged my dht11 on the G2 pin , and here it goes! My transmission and ble connection was a success (more than 8 meters). Thanks for taking the time to answer me that fast ! See ya.

Weijian-Espressif commented 6 years ago

@chegewara Thanks for your help. @Maldus512 @thomasmeon Do you have any other questions?

Maldus512 commented 6 years ago

@Weijian-Espressif I have no more questions, thank you for your time

Alvin1Zhang commented 5 years ago

@Maldus512 Thanks for reporting the issue. Feel free to reopen the issue if this issue still exists. Thanks.