Skip to content
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

BLE: Allow Master as Server #1800

Closed
gfwilliams opened this issue Apr 16, 2020 · 25 comments
Closed

BLE: Allow Master as Server #1800

gfwilliams opened this issue Apr 16, 2020 · 25 comments

Comments

@gfwilliams
Copy link
Member

Needed for Apple ANCS: http://forum.espruino.com/conversations/345949

Maybe a hack where m_central_conn_handle is set to m_peripheral_conn_handle, but IMO it makes sense to modify jswrap_ble_Bluetooth* to grab the connection handle from the referenced object.

Then, we can just provide a NRF.getRemoteGATTServer function to produce a BluetoothRemoteGATTServer with the m_peripheral_conn_handle as the connection handle.

Also, it should make allowing >1 BLE connection possible, which is something that's been requested quite a lot.

@Purple-Tentacle
Copy link

Also, it should make allowing >1 BLE connection possible, which is something that's been requested quite a lot.

I would love that! Bangle.js connected to GadgetBridge and maybe some kind http proxy at the same time.

@jeffmer
Copy link
Contributor

jeffmer commented Apr 21, 2020

After implementing the hack suggested by Gordon for a Puck.js and getting it to work, I have worked out a way of doing the same thing without changing the firmware.

After getting the address of the iPhone using NRF.on(“connect”, function(addr){...})

@gfwilliams
Copy link
Member Author

I have worked out a way of doing the same thing without changing the firmware.

That's great! It looks like your post was unfinished though - what's the trick? :)

@gfwilliams
Copy link
Member Author

I would love that! Bangle.js connected to GadgetBridge and maybe some kind http proxy at the same time.

@Purple-Tentacle that's actually possible right now - you have have one incoming and one outgoing connection - so Bangle.js can be connected to another device such as https://www.espruino.com/BLE+HTTP+Proxy

@jeffmer
Copy link
Contributor

jeffmer commented Apr 21, 2020

Apologies, I posted this on the Forum after Github slowed down and stopped:-

I implemented the hack suggested by Gordon in the firmware for a Puck.js and it does work, however, after some experimentation, I have realised that while interesting - it is not necessary!

Essentially, after getting the address of the iPhone using NRF.on("connect",function(addr){}), you can get a connected BluetoothRemoteGATTServer object using NRF.connect(addr). This causes a disconnect event with reason 8, however, the method returns a connected object that can then be used for bonding. I was able to discover this as I connected the REPL to the physical Puck UART and could thus interrogate objects while bluetooth was connected to the iPhone.

So, I have now been able to get the iPhone notifications on the Bangle without any firmware changes. The only issue with this method is that the startBonding() method promise is fulfilled before bonding is actually complete. This can be solved by waiting a bit and checking the security status too ensure the connection is encrypted.

@gfwilliams
Copy link
Member Author

That's awesome - thanks for finding that out! It almost looks like it's meant to work that way :)

@jeffmer
Copy link
Contributor

jeffmer commented May 5, 2020

Just to record it, I implemented the following function:

JsVar *jswrap_ble_getGattforCentralServer(JsVar *mac) {
  if (jsble_has_peripheral_connection()) {
    m_central_conn_handle = m_peripheral_conn_handle;
    m_peripheral_conn_handle = BLE_CONN_HANDLE_INVALID;
  } else
  {
    return 0;
  }
  JsVar *device = jspNewObject(0, "BluetoothDevice");
  if (!device) return 0;
  jsvObjectSetChild(device, "id", mac);
  JsVar *gatt = jswrap_BluetoothDevice_gatt(device);
  jsvUnLock(device);
  if (!gatt) return 0;
  jsvObjectSetChild(gatt, "connected", jsvNewFromBool(true));
  bleSetActiveBluetoothGattServer(gatt);
  jsvUnLock(gatt);
  return gatt;
}

I compared the performance against the connect back bodge and it is much faster and more reliable grabbing the peripheral connection as above rather than connecting back. The only wrinkle is that when the connection is broken, advertising needs to be restarted explicitly using NRF.wake().

Doing all this properly looks like a major task as the central and peripheral handles are extensively used.

@gfwilliams
Copy link
Member Author

Thanks! That's really interesting - so you don't actually need to call into the softdevice at all, you just need to convince Espruino :)

This is something I'll definitely have a play with later - the >1 connection has been requested a lot, so I'd have to remove the explicit m_central_conn_handle references in those cases anyway.

@jeffmer
Copy link
Contributor

jeffmer commented May 8, 2020

Just tried to build the latest firmware 2.05.61 with the above routine and I am now getting a failure of "Insufficient Resources" when I try to upload using NRF toolbox to the Bangle. Is there any thing I can easily leave out to save space. For example, is there a simple way of leaving TensorFlow out?

@gfwilliams
Copy link
Member Author

Yeah - just edit boards/BANGLEJS.py and remove the TENSORFLOW line then it should build without it :)

Might be your compiler is different? I hit this recently but pulled a few things to try and get it to fit again

@jeffmer
Copy link
Contributor

jeffmer commented May 11, 2020 via email

@gfwilliams
Copy link
Member Author

Thanks - actually just saw a report on the forum that the latest builds aren't working, so it looks like I hit the limit again.

Yeah, it's getting tight on all the boards now :( It's a shame I don't gather any usage stats or I might be able to see if there are any big functions that don't get used.

@jeffmer
Copy link
Contributor

jeffmer commented May 21, 2020

Unfortunately, your commit regarding-

nRF52: If passkey or oob is set in setSecurity, ensure that the UART … 22c5531

broke the ANCS widget. The problem is that the ANCS connection is encrypted and has a passkey, so when the phone disconnects or the widget disconnects due to a KILL event, your update looks the current status of the UART and sees that it does not have encrypted security status- I think that's what happening. In any case, a soft device reset happens which causes a reboot of the Bangle which goes back to the clock app with ANCS widget. So you cannot change apps because you always get a reboot. Reverting the commit fixed the problem.

I tried to reset the security back to unencrypted before a disconnect but this does not help. Disabling the Uart (connectable & programmable) in Settings also does not help.

@gfwilliams
Copy link
Member Author

Argh, sorry - do you have a passkey or oob set then? You should still be able to have security/bonding enabled without the UART being encrypted.

a soft device reset happens which causes a reboot of the Bangle

Hmm - that shouldn't actually happen. Even if there's a softdevice reset it shouldn't reboot the Bangle (was it a reboot via the bootloader?). That sounds like a softdevice assert.

Just noticed this though:

if ((bleStatus&BLE_ENCRYPT_UART) != encryptUart) {

Could be wrong, but I guess that could cause issues. I fixed it in a990338 - do you think you could give that a try?

@jeffmer
Copy link
Contributor

jeffmer commented May 22, 2020

Thanks for looking at this.

Yes, I use a pass key and encryption - required by ANCS.

The reboot happens with the widget running and there is a disconnect from the phone - so bootloader not involved initially.

Also, happens when the widget initiates disconnects before terminating.

Will give the fix a go and get back to you.

@jeffmer
Copy link
Contributor

jeffmer commented May 22, 2020

I am afraid that did not help. As you say, it could be a violated soft device assert - although why does your UART security change make anything different with respect to using the peripheral handle as a central handle?

@gfwilliams
Copy link
Member Author

Argh, that's frustrating. Since you can build you own firmwares please could you just remove this line and see if it fixes it? 22c5531#diff-f121ca081e3cc4feab9641448501dad0R1933

It's really frustrating. I guess the thing to do would be to try this on an nRF52DK or other board where the assert could be reported over UART.

In fact, really we should be able to just ensure any assertion failure is written to the screen on Bangle.js, which would at least help to narrow it down.

@jeffmer
Copy link
Contributor

jeffmer commented May 22, 2020

Hi, I commented out the line with encryptUart=true and it all works again.

I have a Puck with a UART connection which I can use to get more info - if this last test does not help. Is there a make directive to get more error info?

@gfwilliams
Copy link
Member Author

Ok, that's good I guess. If you have a chance, what about removing:

22c5531#diff-f121ca081e3cc4feab9641448501dad0R2160

I guess that would narrow it down to the error being because of the softdevice restart rather than the encrypted UART. Actually come to think of it, presumably you could disable the UART in setServices and it'd work?

I'm afraid there's not much more debug info. If you built it without any of the extras and them omitted RELEASE=1 you'd get assertions in there which may be of some help?

@jeffmer
Copy link
Contributor

jeffmer commented May 23, 2020

Whoops, I thought disabling discoverable and programmable in settings would disable the uart - I had not realised you do it using setServices..

Now when I use setServices(undefined,{uart:false}) in the initialisation of the ANCS widget, I have no problems.

So, I am not sure that this is a bug as if it is a violated softdevice assert, it may be reasonable because of the way I am setting up the iPhone-central-server to Bangle-peripheral-client.

In any case, when I get a chance, I will try removing the line you suggest and let you know the result.

Apologies for probably wasting your time with this.

@gfwilliams
Copy link
Member Author

No problem - it's not wasting my time at all. It's great that you got this working with the iPhone and I don't want to randomly break that :)

@gfwilliams
Copy link
Member Author

Just a note that there's an issue regarding multiple connections at #1360 - hopefully the work to make this non-hacky can be combined with what is needed to implement that

@gfwilliams
Copy link
Member Author

Ok... So I just merged something with support for multiple connections (at least for Puck.js, Pixl.js, MDBT42).

Even if multiple connections aren't supported (like on Bangle.js 1 where I don't for RAM usage) the BluetoothRemoteGATTServer now has a 'handle' field that is then used for all transactions.

So I guess the question is: can we just create a BluetoothRemoteGATTServer with the handle set to the peripheral connection's handle, and will that be enough to allow you to access things like the Time service?

@jeffmer
Copy link
Contributor

jeffmer commented Jun 16, 2022

When I get a chance, I will test to see if it works with IOS time service.

@gfwilliams
Copy link
Member Author

Closing this for now, as we support ANCS/AMS/etc internally now, plus multiple connections.

I'm not sure if this is that useful for anything else, but if it was I'd be really interested to see if just instantiating a BluetoothRemoteGATTServer with its handle set to that of the peripheral would actually be enough

@gfwilliams gfwilliams closed this as not planned Won't fix, can't repro, duplicate, stale Feb 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants