Skip to content

Bluetooth Communication

Youssef-Beltagy edited this page Apr 19, 2021 · 2 revisions

The inhaler/pin and the app will be connected through Bluetooth Low-Energy.

The Inhaler and the Pin are Gatt servers. The Android app is a Gatt Client. The interface between the inhaler/pin and the app are Gatt Services/Characteristics which we need to agree on.

Inhaler-app connection

Ideally, the inhaler will initiate the BLE connection from itself to the app. Once the BLE connection is established, then the inhaler will indicate an IUE to the app. If the inhaler receives an acknowledgement from the app that the indication was transmitted successfully, the inhaler will continue sending IUEs to the app until all IUEs are sent and deleted.

notes:

  • Can the inhaler initiate a BLE connection? (probably yes)
  • But how does the inhaler know which phone to connect to?
  • What did ST mean when they said the MCU can retain connections during standby mode? Ideally, we want to terminate the connection.
  • Assuming the inhaler can initiate a BLE connection, can the app accept this connection?
  • How will the app know it is connected to an inhaler?
  • Will the app be able to receive the indications even though there is a delay in Android between establishing the BLE connection and establishing the GATT connection?
  • We need to design a GATT interface so the Android app and the inhaler can communicate.

Pin-app connection

The app should initiate the BLE connection. Once the app and the pin are connected, the pin will be a passive server which waits for read requests. The app will make read requests as necessary.

Where we are 4/19/2021

BLE operations are asymmetric. There are two sides to BLE: a central side and a peripheral side. The peripheral side advertises itself. The central scans for advertisements to find devices. The central then attempts to connect to a peripheral of its choosing.

Ideal UX

The inhaler/smart-pin should behave just like your Alexa/Car/Headphones. You open Android Bluetooth settings (not even our app) and you pair the inhaler/smart-pin through Android's settings. From then on, Android automatically attempts to connect to our inhaler/smart-pin when they are in range. This means that as soon as the inhaler advertises, the phone attempts to make a connection irrespectively of our app.

The phone remembers the peripheral device and attempts to connect to even after Bluetooth is disabled and then enabled or the device restarts.

After looking online, it seems the way Android does this is by scanning in the background for paired Bluetooth devices. If this is true, it means that the processes of scanning/advertising are not eliminated, but rather that Android manages scanning for us. However, it is hard to confirm this statement without knowing the ins and outs of Android. As a side note, whenever I open my Bluetooth settings, I see my phone attempting to connect with my headphones.

If this UX works, then the app needs to have a Broadcast Receiver with an ACL_connect filter. This notifies the app whenever a Bluetooth device is connected. The app then attempts to identify whether this is the smart-inhaler/smart-pin or not. If the Bluetooth device is a smart-pin/smart-inhaler then the app starts a foreground service and establishes a GATT client/server connection. The app will then receive IUEs as indications from the smart-inhaler. If the smart-pin is connected, the app attempts to retrieve Wearable Data from it. Then the app makes a network call to ClimaCell. Finally, the app saves the IUE with its data to the database.

Current Behavior

This UX is not working as expected. Firstly, I couldn't see my smart-pin from the setting's Bluetooth devices. I figured I could pair without using the settings menu. If I need to, I could make feature to scan and connect to the smart-inhaler/smart-pin (demoed on Thursday) rather than use Android's Bluetooth settings.

To do that, I had the smart-pin encrypt the data to force Android to go through the bonding process. After reading and experimentation, I only added a line to the smart-pin to have it encrypt its communication. This worked (even though I'm not sure what happened). And the devices bonded.

However, this didn't achieve the desired behavior as I demoed on Thursday.

After connecting using our app or nrf connect (not through Android System), our app can automatically connect to the smart-pin as long as the device does not restart and Bluetooth does not go through a disable/enable cycle (our app could do that even without pairing). It is unclear if the app can do that after days/weeks of being disconnected.

This uses a different Android feature from the "Android managed automatic scanning" as I called it. Basically, when I establish a connection in my app, I can set a bool parameter to true and that will tell Android to attempt to reconnect to the device if it disconnects. The underlying concept (to my understanding) is that there is a Bluetooth cache of previously connected Bluetooth devices. Android will respect my wish as long as the smart-pin is in this cache. This cache is cleared when Bluetooth is disabled or when the device restarts. I could not find a guarantee the cache is not cleared simply by waiting a long time (hours/days/weeks).

Analysis and Experimentation

Your car, your headphones, and your Alexa use Classic Bluetooth which is a fundamentally different technology from BLE. So maybe we are using incorrect technology. However, Smart watches should use BLE for communication so there should be hope. Whether they use BLE to establish the connection too is beyond me.

The smart-pin was not paired directly through the Bluetooth settings. Maybe this is important. I tried messing around with the advertisement data and managed to get the smart-pin to show in my Bluetooth settings search for Bluetooth devices. However, when I tried to pair it directly from the settings, the pairing failed and I got an error saying my device is not in "pairing" mode. I am not sure what that means. Maybe this is Classic Bluetooth specific terminology or it is simply another name for advertising that the device is connectable. Or maybe, there is another pairing process that I don't know about since my focus was strictly BLE. I wanted to avoid going down the rabbit hole of Classic Bluetooth. I believe it is better to ask an expert.

I tried having the smart pin directly connect to my phone using its MAC address, but that didn't work. I can't confirm if this is because it needed some prerequisites I don't know about to connect or if connections can't happen without advertisement (what I believe in).

Maybe the smart-pin/smart-inhaler need to be implemented in a specific way so that they can pair through Android System and automatically connect to it. Please see Emil's response to this stack overflow question (read the fine print). Though there were a lot of similar questions, the answer was generally "use Gatt.connect(autoconnect = true)" which is what we already have. This is the feature that is related to the cache that I was talking about.

I don't think it is a good idea to waste any more time on the smart-pin. As in, going forward, we should experiment with the inhaler directly.

Next steps

One obvious next step is to start asking people for help now that our problem is very well-defined.

Implementation-wise, we can:

  1. Investigate Emil's advice and see if we can configure the inhaler as some form of HID device or another alternative.
    • PROS:
      • Ideal user experience
      • The user is less likely to delete the app
      • Managed scanning by Android
      • Low batter consumption
      • We will not need to modify our agreement with the clients
    • CONS:
      • We need to contact Emil to get some clarifications (will attempt to anyway)
      • Puts additional burden on the inhaler side of the team
      • Might require knowledge of some specific BLE details
      • Postpones project risks until near the end of the quarter
  2. Redefine the user experience
    • PROS:
      • Does not consume power
      • May not be complicated depending on how the user experience is redefined
      • Hopefully, we will not depend on anyone's help
    • CONS:
      • We will have to negotiate with the clients
      • We will have to redefine the user experience which may cause additional complications
      • Annoying user experience
      • The flow of information is more complicated and error-prone (I would have to manage everything)
  3. Have the app constantly scan in the background (service)
    • PROS:
      • Should achieve behavior similar to the ideal user experience
      • I can do it without burdening the inhaler side of the team
      • Should achieve the feasibility of the system and give a good idea of how it behaves
      • If I use a foreground service, going forward, we can always assume the app is in the foreground
    • CONS:
      • Android is brutal with background tasks and has a lot of restrictions which are difficult/error-prone to by-pass
      • This drains the battery (the reason Google bullies us)
      • The flow of information is more complicated and error-prone (I would have to manage everything)
      • I may obt to use a foreground service. This means the user will always have an annoying notification, will know we are draining the battery, will hate us and our app, and will ultimately uninstall this app (like I do).
      • I will have to start it when the phone boots, but I don't have a way to start it if Bluetooth is enabled. This means this service can't know if bluetooth is enabled/disabled and has to stay on literally all the time even if Bluetooth is disabled.
      • Depending on what I have to do, the map may require a permission that prevents it from ever being put on Play Store (Google's app store)
      • We might face problems because of Android nasty forks (basically restrictions on foreground services on some phones because of the manufacturer).
  4. As I was writing this document and reviewing my options, I found this option: https://stackoverflow.com/questions/53938353/background-scanning-on-android-8
    • PROS:
      • If this works, it is similar to what we want.
      • I can use this "geofence" in place of the Broadcast receiver.
    • CONS:
      • It will definitely not work on Androids older than 8.
      • I didn't investigate this as much
      • I don't know its limitations
      • I don't know if there is a latency
      • Apparently will not work on older Android devices.
      • It seems to focus on beacons. I don't know if I can get it to work with our inhaler/pin.
      • I'm already stressed about time.

Suggested course of action:

Ignore option 4 due to lack of time. Implement option 3 to assess its feasibility and possibly have an MVP to graduate with. If I fail at option 3, we should fall back to option 2 and renegotiate with the clients.

Concurrently to option 4, we should ask Emil for help. Ask for sources, advice, and direction. See if option 1 is feasible and realistic. How can we do option 1? What should we be careful of?

Resources

I have a bunch of resources. I will add a selection of the best here. For now, I think these are the most important if we must scan:

If we must scan: https://stackoverflow.com/questions/51371372/beacon-scanning-in-background-android-o

Foreground Services are restricted in some phones: https://proandroiddev.com/android-foreground-service-restrictions-d3baa93b2f70

BLE Scan can't restart 5 times in 30 seconds (not an issue). BLE Scans longer than 30 mins will be converted to Opportunistic scans: https://blog.classycode.com/undocumented-android-7-ble-behavior-changes-d1a9bd87d983

Option 4, fencing (for beacons): https://developers.google.com/awareness/overview


To be added in the future

Important keywords

Bluetooth:

BLE:

Central:

Peripheral:

Advertising:

Advertisement data:

Scanning:

Bonding:

Pairing:

Clone this wiki locally