BLE 扫描间隔 Windows 10

Posted

技术标签:

【中文标题】BLE 扫描间隔 Windows 10【英文标题】:BLE Scan Interval Windows 10 【发布时间】:2016-05-18 18:17:49 【问题描述】:

在 Windows 10 上使用Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementWatcher 时,有什么方法可以调整 BLE 广告扫描间隔?在 android 上扫描时,我每 100 毫秒看到一次广告,但在使用 C# 的 Windows 10 上,我每 700 毫秒只会收到一个 BluetoothLEAdvertisementWatcher.Received 事件。

【问题讨论】:

【参考方案1】:

我猜不是。

扫描参数被硬编码为 118.125 ms 的扫描间隔和 18.125 ms 的扫描窗口。

这就是为什么您只能获得所有数据包的 1/7(因为 18.125 / 118.125 约为 1/7)。

但是,您可以使用 DeviceIoControl 来执行更底层的操作。这是一个例子。您必须与您的 BluetoothLEAdvertisementWatcher 并行运行它(例如 BetterScanner.StartScanner(0, 29, 29))。如果两个扫描仪同时处于活动状态,Windows 似乎总是选择“最佳”参数。

DeviceIoControl 永远不会返回,所以我在单独的线程中运行它。如果您需要能够取消扫描,则必须使用重叠 io 才能执行 CancelIoEx。此代码不会检查错误,因此请注意。

using System;
using System.Runtime.InteropServices;
using System.Threading;

class BetterScanner 
    /// <summary>
    /// The BLUETOOTH_FIND_RADIO_PARAMS structure facilitates enumerating installed Bluetooth radios.
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    private struct BLUETOOTH_FIND_RADIO_PARAM
    
        internal UInt32 dwSize;
        internal void Initialize()
        
            this.dwSize = (UInt32)Marshal.SizeOf(typeof(BLUETOOTH_FIND_RADIO_PARAM));
        
    

    /// <summary>
    /// Closes an open object handle.
    /// </summary>
    /// <param name="handle">[In] A valid handle to an open object.</param>
    /// <returns>If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get extended error information, call GetLastError.</returns>
    [DllImport("Kernel32.dll", SetLastError = true)]
    static extern bool CloseHandle(IntPtr handle);

    /// <summary>
    /// Finds the first bluetooth radio present in device manager
    /// </summary>
    /// <param name="pbtfrp">Pointer to a BLUETOOTH_FIND_RADIO_PARAMS structure</param>
    /// <param name="phRadio">Pointer to where the first enumerated radio handle will be returned. When no longer needed, this handle must be closed via CloseHandle.</param>
    /// <returns>In addition to the handle indicated by phRadio, calling this function will also create a HBLUETOOTH_RADIO_FIND handle for use with the BluetoothFindNextRadio function.
    /// When this handle is no longer needed, it must be closed via the BluetoothFindRadioClose.
    /// Returns NULL upon failure. Call the GetLastError function for more information on the error. The following table describe common errors:</returns>
    [DllImport("irprops.cpl", SetLastError = true)]
    static extern IntPtr BluetoothFindFirstRadio(ref BLUETOOTH_FIND_RADIO_PARAM pbtfrp, out IntPtr phRadio);

    [StructLayout(LayoutKind.Sequential)]
    private struct LE_SCAN_REQUEST
    
        internal int scanType;
        internal ushort scanInterval;
        internal ushort scanWindow;
    

    [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
    static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode,
    ref LE_SCAN_REQUEST lpInBuffer, uint nInBufferSize,
    IntPtr lpOutBuffer, uint nOutBufferSize,
    out uint lpBytesReturned, IntPtr lpOverlapped);

    /// <summary>
    /// Starts scanning for LE devices.
    /// Example: BetterScanner.StartScanner(0, 29, 29)
    /// </summary>
    /// <param name="scanType">0 = Passive, 1 = Active</param>
    /// <param name="scanInterval">Interval in 0.625 ms units</param>
    /// <param name="scanWindow">Window in 0.625 ms units</param>
    public static void StartScanner(int scanType, ushort scanInterval, ushort scanWindow)
    
        var thread = new Thread(() =>
        
            BLUETOOTH_FIND_RADIO_PARAM param = new BLUETOOTH_FIND_RADIO_PARAM();
            param.Initialize();
            IntPtr handle;
            BluetoothFindFirstRadio(ref param, out handle);
            uint outsize;
            LE_SCAN_REQUEST req = new LE_SCAN_REQUEST  scanType = scanType, scanInterval = scanInterval, scanWindow = scanWindow ;
            DeviceIoControl(handle, 0x41118c, ref req, 8, IntPtr.Zero, 0, out outsize, IntPtr.Zero);
        );
        thread.Start();
    

【讨论】:

对此进行了测试,效果非常好!现在拿起所有的广告。 谢谢,它似乎确实有效。我不确定如何停止线程,因为 DeviceIoControl 永远不会返回,也没有办法触发它返回。你认为它需要使用 Overlapped IO 来实现异步吗?当涉及到设备驱动程序时,我已经脱离了我的元素。另外,您从哪里获得传递给 DeviceIoControl 的值 0x41118c? 我只在 C 中做过这个,所以如果你使用 C#,你需要使用一些 P/Invokes。您需要创建一个 OVERLAPPED 结构,其中包含一个由 CreateEvent(NULL, FALSE, FALSE, NULL) 分配的事件对象。在 DeviceIoControl 的最后一个参数中传递一个指向该结构的指针。如果一切正常,它将返回 false 和 GetLastError() == ERROR_IO_PENDING。当你想取消时,使用相同的句柄和重叠结构调用 CancelIoEx。在使用 bWait=TRUE 调用 GetOverlappedResult 之后立即。然后你需要在事件上调用 CloseHandle (或者干脆保存它并在下次重复使用)。 有ioctl可以改变发布间隔吗?似乎不是一成不变的。有时使用 BluetoothLEAdvertisementPublisher 会每 100 毫秒发出一次,有时它会每 5-6 秒发出一些广告。 为我工作!但我很想知道您从哪里获得该值 (0x41118c)?【参考方案2】:

如果您设置 SamplingInterval,SignalStrengthFilter 的频率可能会改变。试试这个:

BluetoothLEAdvertisementWatcher watcher=new BluetoothLEAdvertisementWatcher();
watcher.SignalStrengthFilter.SamplingInterval = 100;

【讨论】:

这对我不起作用。仍然收到 700 毫秒的响应。【参考方案3】:

对于 Windows 通用应用程序,请尝试将 StartScanner 更改如下:

public static void StartScanner(int scanType, ushort scanInterval, ushort scanWindow) 

    Action<object> action = (object obj) => 
        BLUETOOTH_FIND_RADIO_PARAM param = new BLUETOOTH_FIND_RADIO_PARAM();
        param.Initialize();
        IntPtr handle;
        BluetoothFindFirstRadio(ref param, out handle);
        uint outsize;
        LE_SCAN_REQUEST req = new LE_SCAN_REQUEST  
            scanType = scanType, 
            scanInterval = scanInterval, 
            scanWindow = scanWindow 
        ;
        DeviceIoControl(handle, 0x41118c, ref req, 8, IntPtr.Zero, 0, out outsize, IntPtr.Zero);
    ;
    Task task = new Task(action,"nothing");
    task.Start();

【讨论】:

以上是关于BLE 扫描间隔 Windows 10的主要内容,如果未能解决你的问题,请参考以下文章

在 iOS 后台实现 BLE 扫描

winform、wpf蓝牙扫描的三种方式

如何使用 BLE 开发 Windows C# 应用程序

由于位置许可,BLE 蓝牙扫描无法在 Android 10 和 11 上运行

Android BLE 扫描屏幕关闭(使用 Galaxy)

主动 BLE 扫描 (BlueZ) - DBus 问题