trobanga / flutter_btleplug

BSD 3-Clause "New" or "Revised" License
29 stars 9 forks source link

Help? #5

Open blopker opened 1 year ago

blopker commented 1 year ago

Hey! I'm writing an app to control a popular ebike for additional functionality. The problem is all the Flutter BLE libraries are pretty broken in various ways (maybe you've seen this too?). They often have weird bugs and function differently on different platforms.

This project is sorely needed! Is there anything I can do to help get it into pub.dev? From what I've seen this could definitely be the "one true" Bluetooth plugin for Flutter/Dart.

trobanga commented 1 year ago

Hi, the current state of BLE in Flutter is a disaster, that's why I had a look into btleplug and I agree that it could be "the one". However, it doesn't align with my current work anymore and at the moment I don't have the time to maintain a flutter plugin but I'd be happy to help.

So, to make this a proper plugin it needs:

  1. a better name
  2. iOS implementation (I might get it from my collegue)
  3. more functionality, at the moment only scanning is implemented
  4. for the native part it would be nice to fetch all dependencies from crates.io instead of local
blopker commented 1 year ago

Ah, fair enough! It seems like you've already done a lot of the hard work integrating with the JVM though.

For the name, I'd guess flutter_btleplug would be the obvious one to me. I suppose it could be useful in just Dart as well, but I'd imagine the user base would be much smaller.

Looking forward to the iOS integration. It seems like iOS and MacOS are so similar getting one done means the other usually works too.

I did notice that the plugin mostly just prints strings from Rust, without giving the Flutter side Dart objects to work with. That's something I might be able to help with.

Anyway, I'll watch this in case you do want to pick it up again.

trobanga commented 1 year ago

The main part of integrating the JVM was done by @qdot, all I did was throwing it into a flutter plugin.

I thought about it and it would be really nice to have a working Flutter BLE plugin. So, I'll try to add the iOS code soon and maybe I can spare a day per month to work on the code. Are you familiar with Rust?

blopker commented 1 year ago

Nice! I have some light hobby experience with Rust. I can probably write some workable glue code if needed. Although I wouldn't claim it would be the most efficient. What do you have in mind? I'm happy to take on specific tickets.

trobanga commented 1 year ago
blopker commented 1 year ago

Nice. I'm playing with it right now. Are you able to get the just build-android command working? After a bunch of issues, I'm now stuck on error occurred: Failed to find tool. Is `aarch64-linux-android31-clang` installed?.

blopker commented 1 year ago

Nevermind! Got it working :)

trobanga commented 1 year ago

Hi, would you still like to contribute? I've managed to build a basic version that is able to scan and connect to devices :)

blopker commented 1 year ago

Oh hell yeah. I make https://github.com/blopker/superduper, and it mostly works with flutter_reactive_ble. There were a lot of issues though. I've managed to work around the showstoppers, but it doesn't do a good job of cleaning up after itself and eventually the app has to be killed and restarted. I'd love to port the app over to this if we can make it more reliable.

I can test out what you've got so far. Before I blindly dig in, what would you like me to look at?

trobanga commented 1 year ago

Ok, great! I think the best way to start is the example and see if it works, I've only tested on Android so far. It doesn't do much, just lists discovered devices and connects by tapping the button.

I have build the plugin such that it remembers discovered devices for 3 seconds and the removes them from the list unless there is an Update event, which is equal for receiving an rssi value. I'm not sure if that is the best solution for all cases and we can make this optional of course.

Then, I've created a few issues that I would address next. But what features do you need for superduper? We could start by implementing them first.

qdot commented 1 year ago

One of the fun portions of this that I may have to work with y'all on is iOS foregrounding.

I just got android backgrounding w/ everything but scanning in the foreground task (which is going to require annoying permissions requests) into my btleplug-using app that's on the play store already. iOS is currently blocked because we need some sort of bluetooth-function-based keepalive for the foreground process in iOS (otherwise it dies in ~30s), but that may need to happen at the btleplug level. I'm looking at putting in some sort of callable, api exposed function that'll trip the keepalive timer, which may be useful for this library. I'll try to remember to update here once I get that figured out.

blopker commented 1 year ago

Nice! I'll check it out this week.

What SD currently uses:

It would also be useful to have subscriptions, but I couldn't get that working reliably with the other packages so for that data I just poll read every few seconds.

trobanga commented 1 year ago

Nice! I'll check it out this week.

What SD currently uses:

* Scan for a specific device name. Example: Find all devices called "Super". All bikes have the same name, with different addresses. Maybe the filter is part of this API, or something that happens in app code?

I'm planning to implement a filter but I haven't thought much about the how yet. So at the moment it is

* Connect to a specific bike.

check

* Read the device ID. SD stores this in a database to key config data with. I noticed with flutter_reactive_ble is that the device IDs are different for the same device between iOS and Android. I haven't looked into why though. I could see that being an issue for some applications.

They are different on iOS and Android. Maybe we can use the address instead.

  • Read from known characteristics. SD doesn't use any discovery functions since all services are known beforehand.

  • Write to known characteristics.

  • Disconnect from device. This is where other solutions start to get wonky since the disconnect isn't always clean. Some people have multiple bikes and need to switch between them as quickly as possible, so this issue pops up often. Although now that I think about it, perhaps it's possible to have multiple bike connections happening at the same time?

I think it's possible to connect to four or five devices at a time.

* The app also needs access to some kind of connection state stream to alert the user when the bike is disconnected/connecting/connected or if bluetooth is disabled.

That sounds useful.

It would also be useful to have subscriptions, but I couldn't get that working reliably with the other packages so for that data I just poll read every few seconds. I'm sure we'll manage that :)

qdot commented 1 year ago

I'm planning to implement a filter but I haven't thought much about the how yet. So at the moment it is

It's usually easiest to hand the user all the advertisements and let them filter.

They are different on iOS and Android. Maybe we can use the address instead.

Address is gonna be different across platforms too. CoreBluetooth and WebBluetooth platforms obscure addresses, but they should remain similar when accessing the same device.

I think it's possible to connect to four or five devices at a time.

This is limited by the radio processing. I start getting data loss at 6.

It would also be useful to have subscriptions, but I couldn't get that working reliably with the other packages so for that data I just poll read every few seconds. I'm sure we'll manage that :)

I'm working on this concurrently in btleplug (subs/notificiations already implemented, just not used much) and my own app, so it'll be tested from multiple directions.

trobanga commented 1 year ago

I'm planning to implement a filter but I haven't thought much about the how yet. So at the moment it is

It's usually easiest to hand the user all the advertisements and let them filter.

Yes, that is something we should implement once we have some experiance with the lib and know that it's a pain point. But I can imagine that it could be a nice feature to filter for the name.

They are different on iOS and Android. Maybe we can use the address instead.

Address is gonna be different across platforms too. CoreBluetooth and WebBluetooth platforms obscure addresses, but they should remain similar when accessing the same device.

So, that means the only way to identify a device with iOS and Android is with it's name?

I just got android backgrounding w/ everything but scanning in the foreground task (which is going to require annoying permissions requests) into my btleplug-using app that's on the play store already. iOS is currently blocked because we need some sort of bluetooth-function-based keepalive for the foreground process in iOS (otherwise it dies in ~30s), but that may need to happen at the btleplug level. I'm looking at putting in some sort of callable, api exposed function that'll trip the keepalive timer, which may be useful for this library. I'll try to remember to update here once I get that figured out.

I'm not sure I understand everything of what you wrote here or maybe I don't understand the terminology of back- and foregrounding. On Android you managed to run btleplug in the background but scanning is still done in the foreground?

qdot commented 1 year ago

So, that means the only way to identify a device with iOS and Android is with it's name?

Name, Manufacturer Data, Advertised Service Data...

On Android you managed to run btleplug in the background but scanning is still done in the foreground?

Whoops, that should've all said "foregrounding".

On mobile apps:

You'll be interested in foregrounding for Bluetooth, because you want your device connection to stay alive when someone sleeps their phone or uses another app. Depending on the situation, it may also be useful to have the ability to scan for devices in the foreground task, though that should be up to the library user as that requires permissions that require extra steps with the relevant store to get (i.e. sending in videos of usage, etc).

Foregrounding works far differently between Android and iOS, you'll have to take into account power/sleep/foreground task considerations per platform, etc...

blopker commented 1 year ago

Cool, I got the example working on Android! I did have to make 2 changes. 1) Add mkdir -p "packages/flutter_btleplug/android/src/main/jniLibs" to build-android-dev.sh since jniLibs didn't exist after first clone. And 2) I needed to add permission_handler: ^10.2.0 to the example pub file and then add await Permission.bluetoothScan.request(); when pressing the scan button. Otherwise, a pretty hairy error was thrown. I guess error handling could be improved in that sense. The average Flutter dev may not be able to understand it.

According to the feature matrix in the readme, I'll need at least writing to characteristics before I can try to integrate it into SD though.

qdot commented 1 year ago

Permissions are more complicated than that, depending on your Android version support. There was a permissions split at Android 12 (API version 30) that means you need to ask for a different set of permissions if you want to support older Android builds.

Here's my code for perms checking, which I shipped last weekend and seems to work back to Android 9. (Breaks on Android 6/7, no one has tested 8/8.1 yet).

https://github.com/intiface/intiface-central/blob/main/lib/intiface_central_app.dart#L99

You'll also need a matching set w/ version conditionals in your manifest:

https://github.com/intiface/intiface-central/blob/main/android/app/src/main/AndroidManifest.xml#L5

trobanga commented 1 year ago

@blopker great that you could make it work! Could you make a PR with your changes? And do you want to try to implement r/w of characteristics?

@qdot Thanks a lot! This will save some time and headaches for sure^^

blopker commented 1 year ago

Any chance you could let me push branches to this repo? That would simplify my workflow to add changes :)

blopker commented 1 year ago

I spent a few hours exploring how to make a BLE server I could test against. The only device I have is my bike and, sadly (I asked!), I'm unable to bring the bike into my house. I tried to run a server on my Mac, but no luck, tried on Android, also no luck. I didn't try iOS. Otherwise, I don't have easy access to a Linux box with a Bluetooth module.

How are y'all making test Bluetooth servers to debug with?

qdot commented 1 year ago

Not sure what you mean by "BLE server".

blopker commented 1 year ago

Sorry, I'm new to the Bluetooth world. I may try to shoe horn web language in... I mean a peripheral? Something that btleplug can connect to. I now see https://pypi.org/project/bless/ might do what I want, but need to try it still.

trobanga commented 1 year ago

It should also be possible to use another phone to simulate or act as a bluetooth sensor.

As for push rights, I would prefer to use PR because otherwise I will lose track of what's happening. I hope that's okay.

blopker commented 1 year ago

That's fair. Although, I didn't mean to say I wanted to push directly to main, only the ability to make branches in this repo and make PRs. Making PRs from my fork and keeping everything in sync is clunky. Is that OK?

Also, have you gotten a phone to work as a peripheral? I haven't had any success yet.

trobanga commented 1 year ago

I haven't tried them but https://play.google.com/store/apps/details?id=com.techbitar.android.sensoduino or https://play.google.com/store/apps/details?id=com.amalanjula.sensorApp&gl=US could work.

I think the only way to give you branch push permission would be to make you a collaborator which gives access to like everything or to create an organisation which we could do...or do you know another way to allow you to push branches?

With github-cli syncing looks pretty easy:

gh repo sync owner/cli-fork -b main