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

TypeError when going from bleak to bleekWare #5

Open
FilMarini opened this issue Oct 30, 2024 · 9 comments
Open

TypeError when going from bleak to bleekWare #5

FilMarini opened this issue Oct 30, 2024 · 9 comments

Comments

@FilMarini
Copy link

Hi,

I've created a very simple app that connects to a device and print whatever the device is sending.

The app works well in Linux using bleak, but when I compile it for Android using bleekWare, it connects ok but when it should print on screen what it receives, it prints TypeError: 'bool' object is not callable

Here's the script Im using:

import asyncio
import toga
from toga import App, Button, Label, Box

if toga.platform.current_platform == 'android':
   from .bleekWare import bleekWareError as BLEError
   from .bleekWare.Client import Client as Client
   from .bleekWare.Scanner import Scanner as Scanner
else:
   from bleak import BleakError as BLEError
   from bleak import BleakClient as Client
   from bleak import BleakScanner as Scanner

# UUIDs for the UART service and characteristic
UART_SERVICE_UUID = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
UART_TX_UUID = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"

class HelloWorld(App):

    def startup(self):
        self.label = Label("Waiting for data...")
        self.button = Button("Connect", on_press=self.connect)
        self.box = Box(children=[self.label, self.button])

        self.main_window = toga.MainWindow(title='BLE UART')
        self.main_window.content = self.box
        self.main_window.show()

    async def connect_to_device(self):
        device_address = "2C:CF:67:97:DE:C7" 
        try:
            async with Client(device_address) as client:
                if client.is_connected():
                    await client.start_notify(UART_TX_UUID, self.notification_handler)
                    self.label.text = "Connected! Waiting for data..."
                
                    # Keep the connection alive
                    while True:
                        await asyncio.sleep(1)
        except Exception as e:
            self.label.text = f"Error: {e}"

    def notification_handler(self, sender, data):
        # Handle incoming data from the Bluetooth device
        message = data.decode('utf-8')
        self.label.text = f"Received: {message}"

    def connect(self, widget):
        # Schedule the async connection task in the event loop
        asyncio.create_task(self.connect_to_device())

def main():
    return HelloWorld()

Any idea why this happens?
Thank you

@MarkusPiotrowski
Copy link
Owner

Dear @FilMarini

I don't think that I can answer your question without having more information. E.g. is it the except block of your code that 'prints' the error or is it printed to the console?

Can you please copy the full stack trace of the exception here? There should be more information about which part of the module throws the error in which line.

BTW, I don't think that you need the if client.is_connected() clause, because the code is within an async with block, so the code within the block will only run if the client is connected. Also, instead of while true: await asyncio.sleep(1) you can just do await asnycio.Future().

@FilMarini
Copy link
Author

FilMarini commented Oct 30, 2024

@MarkusPiotrowski Hi Markus, thanks for the quick answer.

Unfortunately I dont have much more information. The error is not thrown by the except block, but it is visible only in the Android app after connecting to the sender device where the received data should be printed.

Im not sure how I can gather more info. If you have idea on how to do it I'll try.

I'll also implement your suggestions in order to simplify the code. Thanks!

@MarkusPiotrowski
Copy link
Owner

@FilMarini How do you put the app on your phone? If you follow the steps in the Beeware tutorial and do briefcase run android while your phone is connected to your computer, the code should run on your mobile but still shows all the debug messages in the terminal window of the computer. Also, a log file will be written.

@FilMarini
Copy link
Author

@MarkusPiotrowski Oh yeah, it doesnt seem like it says much though.. Here's the output of briefcase run android:

[helloworld] Starting app on moto g84 5G (ZY32JKFGH9) (device ID ZY32JKFGH9)

[helloworld] Installing app...
Stopping old versions of the app... done
Installing new app version... done
Launching app... done

[helloworld] Following device log output (type CTRL-C to stop log)...
===========================================================================
--------- beginning of main
I/mple.helloworld: Late-enabling -Xcheck:jni
I/mple.helloworld: Using CollectorTypeCC GC.
D/nativeloader: Load libframework-connectivity-tiramisu-jni.so using APEX ns com_android_tethering for caller /apex/com.android.tethering/javalib/framework-connectivity-t.jar: ok
D/CompatibilityChangeReporter: Compat change id reported: 171979766; UID 10410; state: ENABLED
D/CompatibilityChangeReporter: Compat change id reported: 242716250; UID 10410; state: ENABLED
W/ziparchive: Unable to open '/data/app/~~BOMez1ZXR2QWqCKxsMzDhg==/com.example.helloworld-8YcdrW1xs7dxtPVmgoThFg==/base.dm': No such file or directory
W/ziparchive: Unable to open '/data/app/~~BOMez1ZXR2QWqCKxsMzDhg==/com.example.helloworld-8YcdrW1xs7dxtPVmgoThFg==/base.dm': No such file or directory
D/nativeloader: Configuring clns-4 for other apk /data/app/~~BOMez1ZXR2QWqCKxsMzDhg==/com.example.helloworld-8YcdrW1xs7dxtPVmgoThFg==/base.apk. target_sdk_version=34, uses_libraries=, library_path=/data/app/~~BOMez1ZXR2QWqCKxsMzDhg==/com.example.helloworld-8YcdrW1xs7dxtPVmgoThFg==/lib/arm64:/data/app/~~BOMez1ZXR2QWqCKxsMzDhg==/com.example.helloworld-8YcdrW1xs7dxtPVmgoThFg==/base.apk!/lib/arm64-v8a, permitted_path=/data:/mnt/expand:/data/user/0/com.example.helloworld
D/nativeloader: Load libframework-connectivity-jni.so using APEX ns com_android_tethering for caller /apex/com.android.tethering/javalib/framework-connectivity.jar: ok
V/GraphicsEnvironment: Currently set values for:
V/GraphicsEnvironment:   angle_gl_driver_selection_pkgs=[]
V/GraphicsEnvironment:   angle_gl_driver_selection_values=[]
V/GraphicsEnvironment: ANGLE GameManagerService for com.example.helloworld: false
V/GraphicsEnvironment: com.example.helloworld is not listed in per-application setting
V/GraphicsEnvironment: Neither updatable production driver nor prerelease driver is supported.
I/Typeface: updateThemeFont new=Rookery-Regular;old=sans-serif;new=Rookery-Stacked;old=
D/AppCompatDelegate: Checking for metadata for AppLocalesMetadataHolderService : Service not found
D/MainActivity: onCreate() start
W/mple.helloworld: Accessing hidden method Landroid/view/View;->computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z (unsupported, reflection, allowed)
W/mple.helloworld: Accessing hidden method Landroid/view/ViewGroup;->makeOptionalFitsSystemWindows()V (unsupported, reflection, allowed)
D/CompatibilityChangeReporter: Compat change id reported: 210923482; UID 10410; state: ENABLED
D/MainActivity: Starting Python
D/nativeloader: Load /data/app/~~BOMez1ZXR2QWqCKxsMzDhg==/com.example.helloworld-8YcdrW1xs7dxtPVmgoThFg==/base.apk!/lib/arm64-v8a/libcrypto_chaquopy.so using ns clns-4 from class loader (caller=/data/app/~~BOMez1ZXR2QWqCKxsMzDhg==/com.example.helloworld-8YcdrW1xs7dxtPVmgoThFg==/base.apk): ok
D/nativeloader: Load /data/app/~~BOMez1ZXR2QWqCKxsMzDhg==/com.example.helloworld-8YcdrW1xs7dxtPVmgoThFg==/base.apk!/lib/arm64-v8a/libssl_chaquopy.so using ns clns-4 from class loader (caller=/data/app/~~BOMez1ZXR2QWqCKxsMzDhg==/com.example.helloworld-8YcdrW1xs7dxtPVmgoThFg==/base.apk): ok
D/nativeloader: Load /data/app/~~BOMez1ZXR2QWqCKxsMzDhg==/com.example.helloworld-8YcdrW1xs7dxtPVmgoThFg==/base.apk!/lib/arm64-v8a/libsqlite3_chaquopy.so using ns clns-4 from class loader (caller=/data/app/~~BOMez1ZXR2QWqCKxsMzDhg==/com.example.helloworld-8YcdrW1xs7dxtPVmgoThFg==/base.apk): ok
D/nativeloader: Load /data/app/~~BOMez1ZXR2QWqCKxsMzDhg==/com.example.helloworld-8YcdrW1xs7dxtPVmgoThFg==/base.apk!/lib/arm64-v8a/libcrypto_python.so using ns clns-4 from class loader (caller=/data/app/~~BOMez1ZXR2QWqCKxsMzDhg==/com.example.helloworld-8YcdrW1xs7dxtPVmgoThFg==/base.apk): ok
D/nativeloader: Load /data/app/~~BOMez1ZXR2QWqCKxsMzDhg==/com.example.helloworld-8YcdrW1xs7dxtPVmgoThFg==/base.apk!/lib/arm64-v8a/libssl_python.so using ns clns-4 from class loader (caller=/data/app/~~BOMez1ZXR2QWqCKxsMzDhg==/com.example.helloworld-8YcdrW1xs7dxtPVmgoThFg==/base.apk): ok
D/nativeloader: Load /data/app/~~BOMez1ZXR2QWqCKxsMzDhg==/com.example.helloworld-8YcdrW1xs7dxtPVmgoThFg==/base.apk!/lib/arm64-v8a/libsqlite3_python.so using ns clns-4 from class loader (caller=/data/app/~~BOMez1ZXR2QWqCKxsMzDhg==/com.example.helloworld-8YcdrW1xs7dxtPVmgoThFg==/base.apk): ok
D/nativeloader: Load /data/app/~~BOMez1ZXR2QWqCKxsMzDhg==/com.example.helloworld-8YcdrW1xs7dxtPVmgoThFg==/base.apk!/lib/arm64-v8a/libpython3.10.so using ns clns-4 from class loader (caller=/data/app/~~BOMez1ZXR2QWqCKxsMzDhg==/com.example.helloworld-8YcdrW1xs7dxtPVmgoThFg==/base.apk): ok
D/nativeloader: Load /data/app/~~BOMez1ZXR2QWqCKxsMzDhg==/com.example.helloworld-8YcdrW1xs7dxtPVmgoThFg==/base.apk!/lib/arm64-v8a/libchaquopy_java.so using ns clns-4 from class loader (caller=/data/app/~~BOMez1ZXR2QWqCKxsMzDhg==/com.example.helloworld-8YcdrW1xs7dxtPVmgoThFg==/base.apk): ok
W/native.stderr: Could not find platform independent libraries <prefix>
W/native.stderr: Could not find platform dependent libraries <exec_prefix>
W/native.stderr: Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]
W/mple.helloworld: type=1400 audit(0.0:18398): avc:  granted  { execute } for  path="/data/data/com.example.helloworld/files/chaquopy/bootstrap-native/arm64-v8a/zlib.so" dev="dm-46" ino=160895 scontext=u:r:untrusted_app:s0:c154,c257,c512,c768 tcontext=u:object_r:app_data_file:s0:c154,c257,c512,c768 tclass=file app=com.example.helloworld
W/mple.helloworld: type=1400 audit(0.0:18399): avc:  granted  { execute } for  path="/data/data/com.example.helloworld/files/chaquopy/bootstrap-native/arm64-v8a/java/chaquopy.so" dev="dm-46" ino=186338 scontext=u:r:untrusted_app:s0:c154,c257,c512,c768 tcontext=u:object_r:app_data_file:s0:c154,c257,c512,c768 tclass=file app=com.example.helloworld
W/mple.helloworld: type=1400 audit(0.0:18406): avc:  granted  { execute } for  path="/data/data/com.example.helloworld/files/chaquopy/bootstrap-native/arm64-v8a/_random.so" dev="dm-46" ino=160705 scontext=u:r:untrusted_app:s0:c154,c257,c512,c768 tcontext=u:object_r:app_data_file:s0:c154,c257,c512,c768 tclass=file app=com.example.helloworld
W/mple.helloworld: type=1400 audit(0.0:18407): avc:  granted  { execute } for  path="/data/data/com.example.helloworld/files/chaquopy/bootstrap-native/arm64-v8a/_sha512.so" dev="dm-46" ino=184910 scontext=u:r:untrusted_app:s0:c154,c257,c512,c768 tcontext=u:object_r:app_data_file:s0:c154,c257,c512,c768 tclass=file app=com.example.helloworld
W/mple.helloworld: type=1400 audit(0.0:18408): avc:  granted  { execute } for  path="/data/data/com.example.helloworld/files/chaquopy/bootstrap-native/arm64-v8a/binascii.so" dev="dm-46" ino=215546 scontext=u:r:untrusted_app:s0:c154,c257,c512,c768 tcontext=u:object_r:app_data_file:s0:c154,c257,c512,c768 tclass=file app=com.example.helloworld
W/mple.helloworld: type=1400 audit(0.0:18409): avc:  granted  { execute } for  path="/data/data/com.example.helloworld/files/chaquopy/bootstrap-native/arm64-v8a/mmap.so" dev="dm-46" ino=184698 scontext=u:r:untrusted_app:s0:c154,c257,c512,c768 tcontext=u:object_r:app_data_file:s0:c154,c257,c512,c768 tclass=file app=com.example.helloworld
W/asset   : seek out of range: want -20, end=22
W/mple.helloworld: type=1400 audit(0.0:18410): avc:  granted  { execute } for  path="/data/data/com.example.helloworld/files/chaquopy/AssetFinder/stdlib-arm64-v8a/fcntl.so" dev="dm-46" ino=186846 scontext=u:r:untrusted_app:s0:c154,c257,c512,c768 tcontext=u:object_r:app_data_file:s0:c154,c257,c512,c768 tclass=file app=com.example.helloworld
D/MainActivity: Running main module helloworld
I/python.stdout: WARNING: Can't find app icon; falling back to default icon
I/python.stdout: Python app launched & stored in Android Activity class
I/python.stdout: Toga app: onCreate
D/MainActivity: onCreate() complete
D/MainActivity: onStart() start
I/python.stdout: Toga app: onStart
D/MainActivity: onStart() complete
D/MainActivity: onResume() start
I/python.stdout: Toga app: onResume
D/MainActivity: onResume() complete
D/CompatibilityChangeReporter: Compat change id reported: 237531167; UID 10410; state: DISABLED
--------- beginning of system
D/ViewRootImpl: update {(0,0)(fillxfill) sim={forwardNavigation} ty=BASE_APPLICATION wanim=0x10302fe
D/ViewRootImpl:   fl=LAYOUT_IN_SCREEN LAYOUT_INSET_DECOR SPLIT_TOUCH HARDWARE_ACCELERATED DRAWS_SYSTEM_BAR_BACKGROUNDS
D/ViewRootImpl:   pfl=NO_MOVE_ANIMATION FORCE_DRAW_STATUS_BAR_BACKGROUND FIT_INSETS_CONTROLLED
D/ViewRootImpl:   bhv=DEFAULT
D/ViewRootImpl:   fitSides=} by setView
D/MainActivity: onTopResumedActivityChanged() start
D/MainActivity: onTopResumedActivityChanged() complete
I/Choreographer: Skipped 72 frames!  The application may be doing too much work on its main thread.
I/AdrenoGLES-0: QUALCOMM build                   : 82d27a90b3, I4ee8f6bced
I/AdrenoGLES-0: Build Date                       : 07/12/23
I/AdrenoGLES-0: OpenGL ES Shader Compiler Version: EV031.35.01.12
I/AdrenoGLES-0: Local Branch                     : 
I/AdrenoGLES-0: Remote Branch                    : refs/tags/AU_LINUX_ANDROID_LA.UM.9.14.11.00.00.571.148
I/AdrenoGLES-0: Remote Branch                    : NONE
I/AdrenoGLES-0: Reconstruct Branch               : NOTHING
I/AdrenoGLES-0: Build Config                     : S P 10.0.7 AArch64
I/AdrenoGLES-0: Driver Path                      : /vendor/lib64/egl/libGLESv2_adreno.so
I/AdrenoGLES-0: PFP: 0x016ee201, ME: 0x00000000
I/OpenGLRenderer: setGrContext nullptr-->validptr
E/OpenGLRenderer: Unable to match the desired swap behavior.
D/MainActivity: onPrepareOptionsMenu() start
I/OpenGLRenderer: Davey! duration=1688ms; Flags=1, FrameTimelineVsyncId=20533039, IntendedVsync=51144520445604, Vsync=51146121201588, InputEventId=0, HandleInputStart=51146121899706, AnimationStart=51146121909394, PerformTraversalsStart=51146122140071, DrawStart=51146189311321, FrameDeadline=51144537667826, FrameInterval=51146121426998, FrameStartTime=22232722, SyncQueued=51146196226164, SyncStart=51146196433404, IssueDrawCommandsStart=51146197283664, SwapBuffers=51146203711321, FrameCompleted=51146208958664, DequeueBufferDuration=183906, QueueBufferDuration=302032, GpuCompleted=51146208958664, SwapBuffersCompleted=51146204526112, DisplayPresentTime=318352029795942780, CommandSubmissionCompleted=51146203711321, 
D/MainActivity: onPrepareOptionsMenu() complete
I/ViewGroup: Adjust the height of content's child to 2034 from 2035 for android.widget.RelativeLayout{17fad4f V.E...... .......D 0,0-505,120}
I/mple.helloworld: Compiler allocated 4653KB to compile void android.view.ViewRootImpl.performTraversals()
D/CompatibilityChangeReporter: Compat change id reported: 263076149; UID 10410; state: ENABLED
D/CompatibilityChangeReporter: Compat change id reported: 265103382; UID 10410; state: ENABLED
D/BluetoothGatt: connect() - device: 2C:CF:67:97:DE:C7, auto: false, eattSupport: false
D/BluetoothGatt: registerApp()
D/BluetoothGatt: registerApp() - UUID=b7fded46-fc26-4269-8e93-50da14888937
D/BluetoothGatt: onClientRegistered() - status=0 clientIf=8
D/BluetoothGatt: onClientConnectionState() - status=0 clientIf=8 device=2C:CF:67:97:DE:C7
D/BluetoothGatt: discoverServices() - device: 2C:CF:67:97:DE:C7
D/BluetoothGatt: onConnectionUpdated() - Device=2C:CF:67:97:DE:C7 interval=6 latency=0 timeout=500 status=0
D/BluetoothGatt: onSearchComplete() = Device=2C:CF:67:97:DE:C7 Status=0
D/BluetoothGatt: onConnectionUpdated() - Device=2C:CF:67:97:DE:C7 interval=36 latency=0 timeout=500 status=0
D/BluetoothGatt: configureMTU() - device: 2C:CF:67:97:DE:C7 mtu: 517
D/BluetoothGatt: cancelOpen() - device: 2C:CF:67:97:DE:C7
D/BluetoothGatt: close()
D/BluetoothGatt: onClientConnectionState() - status=0 clientIf=8 device=2C:CF:67:97:DE:C7
D/BluetoothGatt: unregisterApp() - mClientIf=8
I/ViewGroup: Adjust the height of content's child to 2034 from 2035 for android.widget.RelativeLayout{17fad4f V.E...... ......ID 0,0-1080,2034}
D/ProfileInstaller: Installing profile for com.example.helloworld

and thats what my phone shows:
Screenshot_20241030-122420

@MarkusPiotrowski
Copy link
Owner

Can you change your code like this:
self.button = Button("Connect", on_press=self.connect_to_device)

and

async def connect_to_device(self, widget): ?

For your code I don't see why connect_to_device must run in an additional task. (If it must, then you should make a hard reference to it, otherwise it can happen that the task is deleted by the garbage collector, see Python's documentation for asyncio.create_task())

@FilMarini
Copy link
Author

FilMarini commented Oct 30, 2024

I've updated the code even with the changes you suggested first.

The Linux app using bleak still works well, but the Android app now stays stuck in Connected! Waiting for data..

@MarkusPiotrowski
Copy link
Owner

So, I'm stuck for the moment. I'll try to run your code on my device later. Which version of beeware and Toga are you using?

In the meanwhile, you can try to make a hard reference to the created task:

  1. Revert the changes I suggested above (about button and connect_to_device)
  2. On top-level of your module (e.g. below the UUIDS) add running_tasks = set()
  3. Change your connect method:
def connect(self, widget):
        # Schedule the async connection task in the event loop
        task = asyncio.create_task(self.connect_to_device())
        running_tasks.add(task)
        task.add_done_callback(running_tasks.discard)

Actually, I don't know if this is root of your issue but it would be good to rule it out.

@FilMarini
Copy link
Author

Unfortunately, updating the code with your latest suggestions, the app is still stuck at Waiting for data..

Thanks so much for the support!

I'll paste the latest code just for making sure about the latest changes:

import asyncio
import toga
from toga import App, Button, Label, Box

if toga.platform.current_platform == 'android':
   from .bleekWare import bleekWareError as BLEError
   from .bleekWare.Client import Client as Client
   from .bleekWare.Scanner import Scanner as Scanner
else:
   from bleak import BleakError as BLEError
   from bleak import BleakClient as Client
   from bleak import BleakScanner as Scanner

# UUIDs for the UART service and characteristic
UART_SERVICE_UUID = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
UART_TX_UUID = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"

running_tasks = set()

class HelloWorld(App):

    def startup(self):
        self.label = Label("Waiting for data...")
        self.button = Button("Connect", on_press=self.connect)
        self.box = Box(children=[self.label, self.button])

        self.main_window = toga.MainWindow(title='BLE UART')
        self.main_window.content = self.box
        self.main_window.show()

    async def connect_to_device(self):
        device_address = "2C:CF:67:97:DE:C7"  # Replace with your Pico's Bluetooth address
        try:
            async with Client(device_address) as client:
                await client.start_notify(UART_TX_UUID, self.notification_handler)
                self.label.text = "Connected! Waiting for data..."

                # Keep the connection alive
                await asyncio.Future()
        except Exception as e:
            self.label.text = f"Error: {e}"

    def notification_handler(self, sender, data):
        # Handle incoming data from the Bluetooth device
        message = data.decode('utf-8')
        self.label.text = f"Received: {message}"

    def connect(self, widget):
        # Schedule the async connection task in the event loop
        task = asyncio.create_task(self.connect_to_device())
        running_tasks.add(task)
        task.add_done_callback(running_tasks.discard)

def main():
    return HelloWorld()

@MarkusPiotrowski
Copy link
Owner

@FilMarini Your code (all different versions you tested already) runs fine for me on Android. However, I did test with a different device (not a Pico, that you are obviously using).
So I wonder if the problem is more related to your BLE device or some differences between the Linux and Android implementation of the BLE client.
E.g. the Android backends of Bleak and also of bleekWare doe not read indications (the Linux backend does) . So are you sure that your device sends notifications?
Maybe you can check with some other BLE peripheral that sends notifications or check the BLE code of your Pico.

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

2 participants