Git Product home page Git Product logo

Comments (6)

Dario-Ciceri avatar Dario-Ciceri commented on June 10, 2024 1

What MCU is this on? Can you share a code snippet to reproduce?

I'm using an esp32, I fixed it by using a task on core 0.

What I don't understand is why both the blocking and non-blocking versions, after scanning for "x" seconds, don't return control to the main loop, obviously with two cores there are no problems for the application I need to run however I would like to be able to solve or at least understand if I am doing something wrong.

Part of the code:

//(NOT USED) quick fix blocking scan https://github.com/h2zero/NimBLE-Arduino/issues/297
#include <NimBLEDevice.h>
#include <vector>
using namespace std;

TaskHandle_t scanTask;
bool scanTaskRunning = false;

#define ServerName "VR-PARK"

NimBLEAddress ServerAddress = 0x48472e390db3;

void scanEndedCB(NimBLEScanResults results);

// UUID HID
static NimBLEUUID serviceUUID("1812");
// UUID Report Charcteristic
static NimBLEUUID charUUID("2a4d");

static NimBLEAdvertisedDevice *advDevice;

static bool doConnect = false;
static bool clientConnected = false;
static uint32_t scanTime = 0; /** 0 = scan forever */

class ClientCallbacks : public NimBLEClientCallbacks {
    void onConnect(NimBLEClient *pClient) {
      Serial.println("Connected");
      clientConnected = true;
      /** After connection we should change the parameters if we don't need fast response times.
            These settings are 150ms interval, 0 latency, 450ms timout.
            Timeout should be a multiple of the interval, minimum is 100ms.
            I find a multiple of 3-5 * the interval works best for quick response/reconnect.
            Min interval: 120 * 1.25ms = 150, Max interval: 120 * 1.25ms = 150, 0 latency, 60 * 10ms = 600ms timeout
      */
      pClient->updateConnParams(120, 120, 0, 1);
    };

    void onDisconnect(NimBLEClient *pClient) {
      Serial.print(pClient->getPeerAddress().toString().c_str());
      Serial.println(" Disconnected - Starting scan");
      clientConnected = false;
      //NimBLEDevice::getScan()->start(scanTime, false);
    };

    /** Called when the peripheral requests a change to the connection parameters.
          Return true to accept and apply them or false to reject and keep
          the currently used parameters. Default will return true.
    */
    bool onConnParamsUpdateRequest(NimBLEClient *pClient, const ble_gap_upd_params *params) {
      if (params->itvl_min < 24) { /** 1.25ms units */
        return false;
      } else if (params->itvl_max > 40) { /** 1.25ms units */
        return false;
      } else if (params->latency > 2) { /** Number of intervals allowed to skip */
        return false;
      } else if (params->supervision_timeout > 100) { /** 10ms units */
        return false;
      }

      return true;
    };

    /********************* Security handled here **********************
       ****** Note: these are the same return values as defaults ********/
    uint32_t onPassKeyRequest() {
      //Serial.println("Client Passkey Request");
      /** return the passkey to send to the server */
      return 123456;
    };

    bool onConfirmPIN(uint32_t pass_key) {
      //Serial.print("The passkey YES/NO number: ");
      //Serial.println(pass_key);
      /** Return false if passkeys don't match. */
      return true;
    };

    /** Pairing proces\s complete, we can check the results in ble_gap_conn_desc */
    void onAuthenticationComplete(ble_gap_conn_desc *desc) {
      if (!desc->sec_state.encrypted) {
        //Serial.println("Encrypt connection failed - disconnecting");
        /** Find the client with the connection handle provided in desc */
        NimBLEDevice::getClientByID(desc->conn_handle)->disconnect();
        return;
      }
    };
};

/** Define a class to handle the callbacks when advertisments are received */
class AdvertisedDeviceCallbacks : public NimBLEAdvertisedDeviceCallbacks {

    void onResult(NimBLEAdvertisedDevice *advertisedDevice) {
      //Serial.print("Advertised Device found: ");
      //        ////Serial.println(advertisedDevice->toString().c_str());
      //Serial.printf("name:%s, address:%s ", advertisedDevice->getName().c_str(), advertisedDevice->getAddress().toString().c_str());
      //Serial.printf("UUID:%s\n", advertisedDevice->haveServiceUUID() ? advertisedDevice->getServiceUUID().toString().c_str() : "none");

      if (advertisedDevice->isAdvertisingService(serviceUUID) && advertisedDevice->getAddress().equals(ServerAddress)) {
        //Serial.println("Found Our Service");
        /** stop scan before connecting */
        NimBLEDevice::getScan()->stop();
        /** Save the device reference in a global for the client to use*/
        advDevice = advertisedDevice;
        /** Ready to connect now */
        doConnect = true;
      }
    };
};

int lastJoystickData = -1;

/** Notification / Indication receiving handler callback */
void notifyCB(NimBLERemoteCharacteristic *pRemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify) {
  std::string str = (isNotify == true) ? "Notification" : "Indication";
  str += " from ";
  str += pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress().toString();
  str += ": Service = " + pRemoteCharacteristic->getRemoteService()->getUUID().toString();
  str += ", Characteristic = " + pRemoteCharacteristic->getUUID().toString();
  //    str += ", Value = " + std::string((char*)pData, length);
  //////Serial.println(str.c_str());
  /*////Serial.println("data: ");
    for (int i = 0; i < length; i++) {
    // uint8_tを頭0のstringで表示する
    ////Serial.println("i" + String(i) + ": " + pData[i]);
    }
    ////Serial.print("\nlength: ");
    ////Serial.print(length);
    ////Serial.println();*/

  //lastJoystickData = -1;

  if (pData[0] == 8) {
    Serial.println("LB");
  }
  if (pData[0] == 1) {
    Serial.println("UB");
  }
  if (pData[0] == 233) {
    Serial.println("C");
  }
  if (pData[0] == 234) {
    Serial.println("D");
  }
  if (pData[2] == 239) {
    Serial.println("JU");
  }
  if (pData[2] == 15) {
    Serial.println("JD");
  }
  if (pData[1] == 239) {
    Serial.println("JL");
  }
  if (pData[1] == 15) {
    Serial.println("JR");
  }
  if (pData[0] == 0 && pData[1] == 0 && pData[2] == 0 && pData[3] == 0) {
    Serial.println("S");
  }
}

/** Callback to process the results of the last scan or restart it */
void scanEndedCB(NimBLEScanResults results) {
  Serial.println("Scan Ended");
  scanTaskRunning = false;
}

/** Create a single global instance of the callback class to be used by all clients */
static ClientCallbacks clientCB;

/** Handles the provisioning of clients and connects / interfaces with the server */
bool connectToServer() {
  NimBLEClient *pClient = nullptr;

  /** Check if we have a client we should reuse first **/
  if (NimBLEDevice::getClientListSize()) {
    /** Special case when we already know this device, we send false as the
        second argument in connect() to prevent refreshing the service database.
        This saves considerable time and power.
    */
    pClient = NimBLEDevice::getClientByPeerAddress(advDevice->getAddress());
    if (pClient) {
      if (!pClient->connect(advDevice, false)) {
        //Serial.println("Reconnect failed");
        return false;
      }
      //Serial.println("Reconnected client");
    }
    /** We don't already have a client that knows this device,
        we will check for a client that is disconnected that we can use.
    */
    else {
      pClient = NimBLEDevice::getDisconnectedClient();
    }
  }

  /** No client to reuse? Create a new one. */
  if (!pClient) {
    if (NimBLEDevice::getClientListSize() >= NIMBLE_MAX_CONNECTIONS) {
      //Serial.println("Max clients reached - no more connections available");
      return false;
    }

    pClient = NimBLEDevice::createClient();

    //Serial.println("New client created");

    pClient->setClientCallbacks(&clientCB, false);
    /** Set initial connection parameters: These settings are 15ms interval, 0 latency, 120ms timout.
        These settings are safe for 3 clients to connect reliably, can go faster if you have less
        connections. Timeout should be a multiple of the interval, minimum is 100ms.
        Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 51 * 10ms = 510ms timeout
    */
    pClient->setConnectionParams(12, 12, 0, 51);
    /** Set how long we are willing to wait for the connection to complete (seconds), default is 30. */
    pClient->setConnectTimeout(5);

    if (!pClient->connect(advDevice)) {
      /** Created a client but failed to connect, don't need to keep it as it has no data */
      NimBLEDevice::deleteClient(pClient);
      //Serial.println("Failed to connect, deleted client");
      return false;
    }
  }

  if (!pClient->isConnected()) {
    if (!pClient->connect(advDevice)) {
      //Serial.println("Failed to connect");
      return false;
    }
  }

  //Serial.print("Connected to: ");
  //Serial.println(pClient->getPeerAddress().toString().c_str());
  //Serial.print("RSSI: ");
  //Serial.println(pClient->getRssi());

  /** Now we can read/write/subscribe the charateristics of the services we are interested in */
  NimBLERemoteService *pSvc = nullptr;
  //  NimBLERemoteCharacteristic *pChr = nullptr;
  std::vector<NimBLERemoteCharacteristic *> *pChrs = nullptr;

  NimBLERemoteDescriptor *pDsc = nullptr;

  pSvc = pClient->getService(serviceUUID);
  if (pSvc) { /** make sure it's not null */
    pChrs = pSvc->getCharacteristics(true);
  }

  if (pChrs) { /** make sure it's not null */

    for (int i = 0; i < pChrs->size(); i++) {

      if (pChrs->at(i)->canNotify()) {
        /** Must send a callback to subscribe, if nullptr it will unsubscribe */
        if (!pChrs->at(i)->registerForNotify(notifyCB)) {
          /** Disconnect if subscribe failed */
          pClient->disconnect();
          return false;
        }
      }
    }
  }

  else {
    //Serial.println("DEAD service not found.");
  }

  //Serial.println("Done with this device!");
  return true;
}

void scanTaskFunction(void *pvParameters) {
  scanTaskRunning = true;

  /** Set the IO capabilities of the device, each option will trigger a different pairing method.
      BLE_HS_IO_KEYBOARD_ONLY    - Passkey pairing
      BLE_HS_IO_DISPLAY_YESNO   - Numeric comparison pairing
      BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing
  */
  //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_KEYBOARD_ONLY); // use passkey
  //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison
  /** 2 different ways to set security - both calls achieve the same result.
      no bonding, no man in the middle protection, secure connections.

      These are the default values, only shown here for demonstration.
  */
  //NimBLEDevice::setSecurityAuth(false, false, true);
  NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC);

  /** Optional: set the transmit power, default is 3db */
  NimBLEDevice::setPower(ESP_PWR_LVL_P9); /** +9db */

  /** Optional: set any devices you don't want to get advertisments from */
  // NimBLEDevice::addIgnored(NimBLEAddress ("aa:bb:cc:dd:ee:ff"));
  /** create new scan */
  NimBLEScan *pScan = NimBLEDevice::getScan();

  /** create a callback that gets called when advertisers are found */
  pScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks());

  /** Set scan interval (how often) and window (how long) in milliseconds */
  pScan->setInterval(97);
  pScan->setWindow(37);
  //pScan->setMaxResults(0); // do not store the scan results, use callback only.

  /** Active scan will gather scan response data from advertisers
      but will use more energy from both devices
  */
  pScan->setActiveScan(false);
  /** Start scanning for advertisers for the scan time specified (in seconds) 0 = forever
      Optional callback for when scanning stops.
  */
  while (1) {
    if (!clientConnected) {
      if (!pScan->isScanning()) {
        pScan->start(30, scanEndedCB, false);
      } else {
        vTaskSuspend(scanTask);
      }
    } else {
      scanTaskRunning = false;
      vTaskSuspend(scanTask);
    }
    //pScan->stop();
  }

}

bool BLESetup() {

  NimBLEDevice::init("");

  xTaskCreatePinnedToCore(
    scanTaskFunction,         // Funzione del task
    "ScanTask",               // Nome del task
    10000,                     // Dimensione dello stack del task
    NULL,                      // Parametri del task
    1,                         // Priorità del task
    &scanTask,                 // Handle del task
    0                          // Core del processore su cui eseguire il task (0 o 1)
  );

  return true;
}

bool checkBLEServices() {
  if (doConnect) {
    if (connectToServer()) {
      Serial.println("Success! we should now be getting notifications, scanning for more!");
      doConnect = false;
      clientConnected = true;
      vTaskSuspend(scanTask);
      scanTaskRunning = false;
      return true;
    } else {
      Serial.println("Failed to connect, starting scan");
      doConnect = true;
      if (!scanTaskRunning) {
        vTaskResume(scanTask);
        scanTaskRunning = true;
      }
    }
  } else if (!clientConnected && !scanTaskRunning) {
    Serial.println("Not connected to bluetooth remote, scanning...");
      vTaskResume(scanTask);
      scanTaskRunning = true;
  }
  return false;
}

bool isBluetoothConnected() {
  return clientConnected;
}

setup():
Serial.print("Starting BLE Services"); if (!BLESetup()) { Serial.print("Can't start BLE Services!"); while (1) delay(10); }

p.s. this is the whole library I use to manage a bluetooth remote control, many things maybe you don't care about, look directly for pScan (object I use to call the scan start) if you don't want to look at the whole code!

from nimble-arduino.

Dario-Ciceri avatar Dario-Ciceri commented on June 10, 2024

maybe I found the problem, the scan doesn't stop even after the endscancb is called

from nimble-arduino.

Dario-Ciceri avatar Dario-Ciceri commented on June 10, 2024

nope, even by adding in the callback NimBLEDevice::getScan()->stop(); it doesn't work and keep waiting for something

16:15:50.979 -> Scan Ended
16:15:50.979 -> D NimBLEScan: >> stop()
16:15:50.979 -> D NimBLEScan: << stop()

from nimble-arduino.

Dario-Ciceri avatar Dario-Ciceri commented on June 10, 2024

this #297 helps but it's not perfect btw

from nimble-arduino.

h2zero avatar h2zero commented on June 10, 2024

What MCU is this on? Can you share a code snippet to reproduce?

from nimble-arduino.

h2zero avatar h2zero commented on June 10, 2024

A quick look suggests that the scan task is being suspended in various situations but never resumed so the scan is never restarted.

from nimble-arduino.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.