deviceplug / btleplug

Rust Cross-Platform Host-Side Bluetooth LE Access Library

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

BLE crash in connect() when funcion is run by an inner Module

juanespj opened this issue · comments

I am learning Rust and BLE at the same time, I am basing my work until now with the example subscribe_notify_characteristic
I moved it inside of an async function to be called by the main loop.

`
use btleplug::api::{Central, CharPropFlags, Manager as _, Peripheral, ScanFilter};
use btleplug::platform::Manager;
use futures::stream::StreamExt;
use std::error::Error;
use std::time::Duration;
use tokio::time;
use uuid::Uuid;

/// Only devices whose name contains this string will be tried.
const PERIPHERAL_NAME_MATCH_FILTER: &str = "Feeder";
/// UUID of the characteristic for which we should subscribe to notifications.
const NOTIFY_CHARACTERISTIC_UUID: Uuid = Uuid::from_u128(0x6e400002_b534_f393_67a9_e50e24dccA9e);

#[tokio::main]
async fn main() -> Result<(), Box> {
initble().await.expect("err");
Ok(())
}

async fn initble() -> Result<(), Box> {
pretty_env_logger::init();

let manager = Manager::new().await?;
let adapter_list = manager.adapters().await?;
if adapter_list.is_empty() {
    eprintln!("No Bluetooth adapters found");
}

for adapter in adapter_list.iter() {
    println!("Starting scan...");
    adapter
        .start_scan(ScanFilter::default())
        .await
        .expect("Can't scan BLE adapter for connected devices...");
    time::sleep(Duration::from_secs(2)).await;
    let peripherals = adapter.peripherals().await?;

    if peripherals.is_empty() {
        eprintln!("->>> BLE peripheral devices were not found, sorry. Exiting...");
    } else {
        // All peripheral devices in range.
        for peripheral in peripherals.iter() {
            let properties = peripheral.properties().await?;
            let is_connected = peripheral.is_connected().await?;
            let local_name = properties
                .unwrap()
                .local_name
                .unwrap_or(String::from("(peripheral name unknown)"));
           
            // Check if it's the peripheral we want.
            if local_name.contains(PERIPHERAL_NAME_MATCH_FILTER) {
                println!("Found matching peripheral {:?}...", &local_name);
                if !is_connected {
                    // Connect if we aren't already connected.
                    if let Err(err) = peripheral.connect().await {
                        eprintln!("Error connecting to peripheral, skipping: {}", err);
                        continue;
                    }
                }
                let is_connected = peripheral.is_connected().await?;
                println!(
                    "Now connected ({:?}) to peripheral {:?}.",
                    is_connected, &local_name
                );
                if is_connected {
                    println!("Discover peripheral {:?} services...", local_name);
                    peripheral.discover_services().await?;
                    for characteristic in peripheral.characteristics() {
                        println!("Checking characteristic {:?}", characteristic);
                        // Subscribe to notifications from the characteristic with the selected
                        // UUID.
                        if characteristic.uuid == NOTIFY_CHARACTERISTIC_UUID
                            && characteristic.properties.contains(CharPropFlags::NOTIFY)
                        {
                            println!("Subscribing to characteristic {:?}", characteristic.uuid);
                            peripheral.subscribe(&characteristic).await?;
                            // Print the first 4 notifications received.
                            let mut notification_stream =
                                peripheral.notifications().await?.take(4);
                            // Process while the BLE connection is not broken or stopped.
                            while let Some(data) = notification_stream.next().await {
                                println!(
                                    "Received data from {:?} [{:?}]: {:?}",
                                    local_name, data.uuid, data.value
                                );
                            }
                        }
                    }
                    println!("Disconnecting from peripheral {:?}...", local_name);
                    peripheral.disconnect().await?;
                }
            } else {
                //   println!("Skipping unknown peripheral {:?}", peripheral);
            }
        }
    }
}
Ok(())

}`

this worked perfectly, just like in the example.

then I moved this function inside of a module

`use btleplug::api::{Central, CharPropFlags, Manager as _, Peripheral, ScanFilter};
use btleplug::platform::Manager;
use futures::stream::StreamExt;
use std::error::Error;
use std::time::Duration;
use tokio::time;
use uuid::Uuid;

/// Only devices whose name contains this string will be tried.
const PERIPHERAL_NAME_MATCH_FILTER: &str = "Feeder";
/// UUID of the characteristic for which we should subscribe to notifications.
const NOTIFY_CHARACTERISTIC_UUID: Uuid = Uuid::from_u128(0x6e400002_b534_f393_67a9_e50e24dccA9e);
// const NOTIFY_CHARACTERISTIC_UUID: Uuid = Uuid::from_u128(0x65f52242_2af2_473b_a939_57e5753c92b5);

pub async fn initble() -> Result<(), Box> {
use btleplug::api::{Central, CharPropFlags, Manager as _, Peripheral, ScanFilter};
use btleplug::platform::Manager;
use futures::stream::StreamExt;
use std::error::Error;
use std::time::Duration;
use tokio::time;
use uuid::Uuid;

/// Only devices whose name contains this string will be tried.
const PERIPHERAL_NAME_MATCH_FILTER: &str = "Feeder";
/// UUID of the characteristic for which we should subscribe to notifications.
const NOTIFY_CHARACTERISTIC_UUID: Uuid = Uuid::from_u128(0x6e400002_b534_f393_67a9_e50e24dccA9e);
// const NOTIFY_CHARACTERISTIC_UUID: Uuid = Uuid::from_u128(0x65f52242_2af2_473b_a939_57e5753c92b5);

pub async fn initble() -> Result<(), Box> {
/// .... same code ... ////
Ok(())
}`

called this funtion from the main loop
`mod appmod { pub mod btcomm; }

async fn main()-> Result<(), Box> {
appmod::btcomm::initble().await.expect("error");
Ok(())
}
`
also worked as expected...

when I call this function inside of an Impl fn so just a sync funtion through
block_on(btcomm::initble()).expect("error");

The program crashes when connecting just after the

the block_on should mimic the await function right??

thanks for the help!!

commented

Do you have a backtrace available for the crash?

Hi qdot,
i tried running with Backtrace but it doesnt really crash the program, it gets stuck I have uploaded the program to my GIT
if you run the app then click BLE on the top menu it starts the routine

also tried debugging but cant really follow any sequence of what is going on
link

the main function has commented out line 15 to call BLE function (this one works)
call the BLE function in line 183 in mod.rs (appmod) (doesnt work)

maybe this helps?

Loaded 'C:\Windows\System32\IPHLPAPI.DLL'. 
Loaded 'C:\Windows\System32\nsi.dll'. 
Unloaded 'C:\Windows\System32\nsi.dll'.
Loaded 'C:\Windows\System32\nsi.dll'. 
Loaded 'C:\Windows\System32\BthRadioMedia.dll'. 
Loaded 'C:\Windows\System32\Windows.Devices.Bluetooth.dll'. 
Loaded 'C:\Windows\System32\Microsoft.Bluetooth.Proxy.dll'. 
27
Exception thrown at 0x00007FF8D647474C (KernelBase.dll) in jeapp.exe: WinRT originate error - 0x8000000B : 'The operation attempted to access data outside the valid range'.
1350
Exception thrown at 0x00007FF8D647474C (KernelBase.dll) in jeapp.exe: WinRT originate error - 0x80010108 : 'The object invoked has disconnected from its clients.'.
Exception thrown at 0x00007FF8D647474C in jeapp.exe: Microsoft C++ exception:  ?? ::st_panic at memory location 0x000000A9CDD93998.

thank you!!

Hi Qdot,
I just found a way around this
instead of using async to run this function I pushed it into a new thread
thread::spawn(|| blesys::BLESys::run(newsys));

commented

Huh, interesting. Do you know if your block_on was spawning a single threaded or multithreaded runtime?

I cant really say, but the running as async on the block_on inside the module didnt work, sorry im just learning all this I believe it was a single thread.
sorry im just learning all this