Comments (6)
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.
maybe I found the problem, the scan doesn't stop even after the endscancb is called
from nimble-arduino.
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.
this #297 helps but it's not perfect btw
from nimble-arduino.
What MCU is this on? Can you share a code snippet to reproduce?
from nimble-arduino.
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)
- Support for new ESP32 Arduino core (3.x) and IDF (5.1) HOT 7
- [Feature request] Support for ESP32-C6 HOT 17
- ESP_ERROR_CHECK failed on platformio, [email protected] HOT 1
- Is it possible to advertise multiple 128bit services with data? HOT 1
- no advertising with ESP32-C6 HOT 6
- Set Manufacturer Data - Bug HOT 1
- Platformio, arduino and esp32 - pinning nimble to core and debug HOT 3
- onAuthenticationComplete() never called HOT 2
- ESP32-S3 crashing with the examples HOT 2
- Device name not showed at first connection HOT 3
- Allow only certain bonded client to connect to NimBLEServer. Whitelist in advertising does not work for already bonded peers
- Changing CPU frequency causes crash HOT 4
- Notification 8 byte data size HOT 1
- how to get faster reconnects from deep sleep HOT 3
- ble does not work when cpu set to 40mhz or bellow HOT 2
- notify not updating values in nRF Connect HOT 2
- Is it possible to advertise the device name as a client? HOT 2
- How to achieve long-distance connection communication? HOT 3
- Rapid and Repeated: E NimBLEClient: Connection failed; status=574 Connection Failed to be Established HOT 2
- How to obtain client from existing server connection? HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from nimble-arduino.