Skip to content

begin() doesn't work after end() on Nano BLE 33, have to power cycle #65

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
rmlearney-digicatapult opened this issue Apr 7, 2020 · 14 comments · Fixed by #92
Closed
Labels
conclusion: resolved Issue was resolved topic: code Related to content of the project itself type: imperfection Perceived defect in any part of project

Comments

@rmlearney-digicatapult
Copy link

rmlearney-digicatapult commented Apr 7, 2020

Hi,

The begin() function does not behave as expected following an end() and instead I have to power cycle. This is similar to #33

I've created two minimal examples for people to try below:

Example 1:

  1. Initialise BLE
  2. Call BLE.disconnect() 10 seconds after connected to central
  3. Re-advertise 15 seconds later to allow reconnection

Expected behaviour - disconnect from central and allow reconnection after re-advertising
Actual behaviour - works as expected

#include <ArduinoBLE.h>

BLEService TestService("DEAD");
BLECharacteristic TestData("BEEF", BLEIndicate | BLENotify, 2, true);
BLEDescriptor TestDescriptor("BEEF", "Test");

uint32_t genericTimer = 0;
bool wasConnected = false;

void setup(){
    initBLE();
}

void loop(){
    BLEDevice central = BLE.central(); // begin listening for centrals to connect

    if(central){
        genericTimer = millis();
        while(central.connected()){
            if(millis() - genericTimer >= 10000){ // Wait 10 seconds after connect
                BLE.disconnect(); // Disconnect BLE
                wasConnected = true;
            }
        }
    }

    if(wasConnected){
        genericTimer = millis();
        while(millis() - genericTimer <= 15000){} // Wait 15 seconds after disconnect
        wasConnected = false;
        BLE.advertise();
    }
}

void initBLE(void){
    BLE.begin();
    BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler);
    BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler);
    BLE.setLocalName("TestName");
    BLE.setDeviceName("Test Device Name");
    TestService.addCharacteristic(TestData);
    TestData.addDescriptor(TestDescriptor);
    BLE.addService(TestService);
    BLE.setAdvertisedService(TestService);
    BLE.advertise();
}

Example 2:

  1. Initialise BLE
  2. Disconnect and end() after 10 seconds of connection to central
  3. Wait 15 seconds and try to re-start BLE system again

Expected behaviour - system disconnects, then ends BLE service, and restarts it correctly
Actual behaviour - unable to restart BLE with begin() after calling end()

#include <ArduinoBLE.h>

BLEService TestService("DEAD");
BLECharacteristic TestData("BEEF", BLEIndicate | BLENotify, 2, true);
BLEDescriptor TestDescriptor("BEEF", "Test");

uint32_t genericTimer = 0;
bool wasConnected = false;

void setup(){
    initBLE();
}

void loop(){
    BLEDevice central = BLE.central(); // begin listening for centrals to connect

    if(central){
        genericTimer = millis();
        while(central.connected()){
            if(millis() - genericTimer >= 10000){ // Wait 10 seconds after connect
                BLE.disconnect(); // Disconnect BLE
                BLE.end(); // End BLE service
                wasConnected = true;
            }
        }
    }

    if(wasConnected){
        genericTimer = millis();
        while(millis() - genericTimer <= 15000){} // Wait 15 seconds after disconnect
        wasConnected = false;
        initBLE();
    }
}

void initBLE(void){
    BLE.begin();
    BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler);
    BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler);
    BLE.setLocalName("TestName");
    BLE.setDeviceName("Test Device Name");
    TestService.addCharacteristic(TestData);
    TestData.addDescriptor(TestDescriptor);
    BLE.addService(TestService);
    BLE.setAdvertisedService(TestService);
    BLE.advertise();
}
@rmlearney-digicatapult
Copy link
Author

rmlearney-digicatapult commented Apr 8, 2020

Probing BLELocalDevice::begin() in BLELocalDevice.cpp with digitalWrite(LED_BUILTIN) inserts shows that the call to HCI.reset() is the source of the failure to restart after end().

So for some reason the system is not responding to the reset opcode the second time round.

Any help welcome. @facchinm?

@rmlearney-digicatapult
Copy link
Author

rmlearney-digicatapult commented Apr 8, 2020

Continuing to probe. HCIClass::poll is failing to enter while (HCITransport.available()) second time round.

Looks like HCI is not available after end(), and isn't getting reset for some reason.

@rmlearney-digicatapult
Copy link
Author

rmlearney-digicatapult commented Apr 8, 2020

For those who want to test this, use the following sketch:

#include <ArduinoBLE.h>

BLEService TestService("DEAD");
BLECharacteristic TestData("BEEF", BLEIndicate | BLENotify, 2, true);
BLEDescriptor TestDescriptor("BEEF", "Test");

uint32_t genericTimer = 0;
bool wasConnected = false;

void setup(){

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);

  initBLE();

  delay(500);
  digitalWrite(LED_BUILTIN, LOW);
  delay(500);
  digitalWrite(LED_BUILTIN, HIGH);
}

void loop(){
  BLEDevice central = BLE.central(); // begin listening for centrals to connect

  if(central){
    genericTimer = millis();
    while(central.connected()){
      if(millis() - genericTimer >= 5000){ // Wait 5 seconds
        BLE.disconnect(); // Disconnect BLE - LED should go out
        BLE.end();
        digitalWrite(LED_BUILTIN, LOW); // Doesn't go out - have to do it manually
        wasConnected = true;
      }
    }
  }

  if(wasConnected){
    genericTimer = millis();
    while(millis() - genericTimer <= 5000){} // Wait 5 seconds
      wasConnected = false;
      initBLE();
  }
}

void initBLE(void){ 
  if(BLE.begin()){
    digitalWrite(LED_BUILTIN, HIGH);
  }
  BLE.setLocalName("TestName");
  BLE.setDeviceName("Test Device Name");
  TestService.addCharacteristic(TestData);
  TestData.addDescriptor(TestDescriptor);
  BLE.addService(TestService);
  BLE.setAdvertisedService(TestService);
  BLE.advertise();
}

And insert digitalWrite(LED_BUILTIN, HIGH); at line 81 in BLELocalDevice.cpp

This will show the LED re-lighting after end().

But moving that digitalWrite(LED_BUILTIN, HIGH); to line 86 will fail, showing that is the (first at least) code block that fails the second time round.

Or to go one step further, put that digitalWrite(LED_BUILTIN, HIGH); on line 112 of HCI.cpp inside the HCIClass::poll function - the LED will light. But putting it just inside the while (HCITransport.available()) conditional will fail showing it's not entering this section of code.

Unfortunately that's as far as I can get. I don't know enough to explain why the HCI isn't available after calling end().

@per1234 per1234 added the type: imperfection Perceived defect in any part of project label Apr 14, 2020
@drewbus-drewbus
Copy link

I'm experiencing the same bug.

@navrit
Copy link

navrit commented May 12, 2020

I'm also experiencing this bug, also using the Nano 33 BLE Sense and with the latest commit.

@rmlearney-digicatapult & @drewbus-drewbus, I assume you are also trying to turn the radio on and off at will.

I was looking at the NINA B306 schematics, it shows a hardware reset pin (RESET_N) in C3/C4 from the NINA at pin 19 to P0.18. It's not clear to me whether this input is accessible to us currently from the U-blox NINA-B3 Series datasheet, see 1.5.1.
EDIT: This was a dead-end. Later I found that the RESET_N pin (P0.18) is actually the one connected to the reset button... see: here

For others wondering about the pin mapping between the nRF52840 and the Nano 33 BLE Sense, here's a useful thread.

@per1234 @facchinm Could you provide assistance? Turning the radio off and on would be very useful.

Let me know if I can help with testing!

@ebarnette-ms
Copy link

I am trying to write a reset function that turns BLE off and then back on again. I'm being blocked by this bug as well!

Let me know if there's anything I can do to assist @rmlearney-digicatapult

@rmlearney-digicatapult
Copy link
Author

@sastorer-ms I'm definitely no expert when it comes to C++ but if you could possibly poke around and confirm my findings that could really help.

Unfortunately I don't know how to raise the priority of this issue with the Arduino team but would love to understand if this is on their backlog.

@fgaetani
Copy link

fgaetani commented Jul 1, 2020

I have the same bug, what can be the solution?

@rmlearney-digicatapult
Copy link
Author

rmlearney-digicatapult commented Jul 1, 2020

Hi @fgaetani I'm afraid I don't know. I think we need input from one of the Arduino team like @facchinm or @per1234 because this library seems a little neglected - PRs appear to be sitting unmerged and unreviewed after a number of months.

@facchinm
Copy link
Contributor

facchinm commented Jul 2, 2020

We are actively investigating all the reports and will post a response later today 🙂

@facchinm
Copy link
Contributor

facchinm commented Jul 2, 2020

The problem here is that the bleLoop thread is getting terminated on end()

bleLoopThread.terminate();
CordioHCIHook::getDriver().terminate();

The CordioHCIHook terminate(), however, doesn't shut down the radio, so calling BLE.end() right now will not have the expected behaviour.

Moreover, starting a thread twice is forbidden in mbed (https://os.mbed.com/docs/mbed-os/v6.0/mbed-os-api-doxy/classrtos_1_1_thread.html#ad315c4f13f31fffde51040cbf00518cb) so the second begin() will fail anyway.

I'm going to post a patch for the thread start/stop but the only way to have a real end() functionality is to patch Cordio sources (and could take longer).

@polldo

@rmlearney-digicatapult
Copy link
Author

rmlearney-digicatapult commented Jul 2, 2020

@facchinm thank you!

Would it make sense to create a new functionality called .off(), separate from .end()

.end() would terminate the loop but leave the radio on, and could be restarted by .begin()

.off() would terminate the loop and turn the radio off, and could also be restarted by .begin()

@facchinm
Copy link
Contributor

facchinm commented Jul 2, 2020

The scope of this library is to abstract the underlying hardware as much as possible, thus a proper end() should be implemented in cordio, no need to add other useless APIs 🙂

Anyway, I added #92 to fix the end() -> begin() hang, pleas egive it a try so we can merge it.

@rmlearney-digicatapult
Copy link
Author

Thank you @facchinm ! I'll try tonight.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
conclusion: resolved Issue was resolved topic: code Related to content of the project itself type: imperfection Perceived defect in any part of project
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants
@navrit @per1234 @facchinm @fgaetani @rmlearney-digicatapult @ebarnette-ms @drewbus-drewbus and others