Why value does not fall within the expected range

2019-06-03 01:15发布

I would like to keep on reading characteristic/set value changed event handlers for characteristics from my BLE 4.0 device, by using the ValueChanged callback in Universal Windows Platform C# in Visual Studio 2017.

I followed some tutorial from these sites: Damian Blog's Windows Universal with BLE, Bluetooth Gatt's Git Hub, Bluetooth Generic Attribute Profile - Heart Rate Service and Dr. Jukka's mobile Blog on BLE. All of them are using ValueChanged and I have tried to follow what they did.

Unfortunately, instead of ValueChanged being triggered, I receive the following error when using the ValueChanged callback.

System.ArgumentException: 'Value does not fall within the expected range.'

This line of code is producing the error:

characteristic.ValueChanged += Oncharacteristic_ValueChanged;

Here is more details of my source code:

NOTE: I am using COM 7 for my dongler and my program could discover the BLE's device name, and could discover the Uuid of the services and characteristics.

    public List<string> serviceList = new List<string>();
    public List<string> characteristicList = new List<string>();
    public BluetoothLEDevice myDevice { get; set; }

    public MainPage()
    {
        this.InitializeComponent();
    }

        private async void Page_Loaded(object sender, RoutedEventArgs e)
    {
        // Find the com port
        string selector = SerialDevice.GetDeviceSelector("COM7");
        DeviceInformationCollection devices = await DeviceInformation.FindAllAsync(selector);
        if (devices.Count > 0)
        {
            var dialog = new MessageDialog("Com Device found");
            await dialog.ShowAsync();

            DeviceInformation deviceInfo = devices[0];
            SerialDevice serialDevice = await SerialDevice.FromIdAsync(deviceInfo.Id);
            serialDevice.BaudRate = 9600;
            serialDevice.DataBits = 8;
            serialDevice.StopBits = SerialStopBitCount.One;
            serialDevice.Parity = SerialParity.None;
        }
        else
        {
            MessageDialog popup = new MessageDialog("Sorry, no device found.");
            await popup.ShowAsync();
        }

        // After com port is found, search for device
        foreach (DeviceInformation di in await DeviceInformation.FindAllAsync(BluetoothLEDevice.GetDeviceSelector()))
        {
            BluetoothLEDevice bleDevice = await BluetoothLEDevice.FromIdAsync(di.Id);

            // Display BLE device name
            var dialogBleDeviceName = new MessageDialog("BLE Device Name " + bleDevice.Name);
            await dialogBleDeviceName.ShowAsync();

            myDevice = bleDevice;
        }

        // Check device connection
        myDevice.ConnectionStatusChanged += OnConnectionStatusChanged;

        foreach (var service in myDevice.GattServices)
        {
            serviceList.Add(service.Uuid.ToString());

            // Verify if service is discovered by displaying a popup
            MessageDialog serviceUuidPopUp = new MessageDialog("Adding Service Uuid to list " + service.Uuid.ToString() );
            await serviceUuidPopUp.ShowAsync();

            foreach (var characteristic in service.GetAllCharacteristics())
            {
                var characteristicUuid = characteristic.Uuid.ToString().ToLowerInvariant();
                characteristicList.Add(characteristicUuid);

                // Verify if characteristic is discovered by displaying a popup 
                MessageDialog charUuidPopUp = new MessageDialog("Adding characteristic Uuid to list " + characteristicUuid);
                await charUuidPopUp.ShowAsync();

                // set value changed event handlers for characteristics
                characteristic.ValueChanged += Oncharacteristic_ValueChanged;

            }
        }
    }

    private void OnConnectionStatusChanged(BluetoothLEDevice sender, object args)
    {
        if (sender.ConnectionStatus == BluetoothConnectionStatus.Connected)
        {
            System.Diagnostics.Debug.WriteLine("Connected");
        }
        else
        {
            System.Diagnostics.Debug.WriteLine("Disconnected");
        }
    }    

    private void Oncharacteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)
    {
        byte[] data = new byte[args.CharacteristicValue.Length];
        DataReader.FromBuffer(
            args.CharacteristicValue).ReadBytes(data);
        string text = Encoding.UTF8.GetString(data, 0, data.Length);
    }

UPDATE 1 I tried to check Characteristic Properties before set value changed event handlers for my characteristics by following the answer given by rudi belt on SO.

if (characteristic.CharacteristicProperties == (GattCharacteristicProperties.Read | GattCharacteristicProperties.Notify))
                {
                    characteristic.ValueChanged += Oncharacteristic_ValueChanged;
                }   

Unfortunately, this IF statement is not executed.

UPDATE 2 I have tried to remove ALL the codes inside Oncharacteristic_ValueChanged method. But it still gives me the same error

System.ArgumentException: 'Value does not fall within the expected range.'

I have been spending a lot of time trying to solve this problem. I will be very happy if anyone can help me on this. Thank you!

1条回答
戒情不戒烟
2楼-- · 2019-06-03 01:31

Reading your efforts in the former question I can provide a working example, but first some explanation. myDevice.ConnectionStatusChanged is not needed, it is only used to notice a connection is lost or connected. You have to connect to your device first and handle things in the connection method.

After you have succeeded in connecting you have to get the service that contains the characteristic you want to use for read, write, notify or indicate.

When you have selected the service You can get the characteristics of that service.

Select the characteristic by Uuid, or in my example with CharacteristicProperties.HasFlag. This flag in my example is Notify. In the code comments you find extra info.

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
using Windows.Devices.Enumeration;
using Windows.UI.Popups;
using Windows.UI.Xaml.Controls;

namespace App1
{
public sealed partial class MainPage : Page
{
   GattDeviceServicesResult serviceResult = null;
  private BluetoothLEDevice myDevice;
  private GattCharacteristic selectedCharacteristic;

  public MainPage()
  {
     this.InitializeComponent();
     ConnectDevice();
  }

  private async void ConnectDevice()
  {
     //This works only if your device is already paired!
     foreach (DeviceInformation di in await DeviceInformation.FindAllAsync(BluetoothLEDevice.GetDeviceSelector()))
     {
        BluetoothLEDevice bleDevice = await BluetoothLEDevice.FromIdAsync(di.Id);
        // Display BLE device name
        var dialogBleDeviceName = new MessageDialog("BLE Device Name " + bleDevice.Name);
        await dialogBleDeviceName.ShowAsync();
        myDevice = bleDevice;
     }

     if (myDevice != null)
     {
        int servicesCount = 3;//Fill in the amount of services from your device!!!!!
        int tryCount = 0;
        bool connected = false;
        while (!connected)//This is to make sure all services are found.
        {
           tryCount++;
           serviceResult = await myDevice.GetGattServicesAsync();
           if (serviceResult.Status == GattCommunicationStatus.Success && serviceResult.Services.Count >= servicesCount)
           {
              connected = true;
              Debug.WriteLine("Connected in " + tryCount + " tries");
           }
           if (tryCount > 5)//make this larger if faild
           {
              Debug.WriteLine("Failed to connect to device ");
              return;
           }
        }
        if (connected)
        {
           for (int i = 0; i < serviceResult.Services.Count; i++)
           {
              var service = serviceResult.Services[i];
              //This must be the service that contains the Gatt-Characteristic you want to read from or write to !!!!!!!.
              string myServiceUuid = "0000ffe0-0000-1000-8000-00805f9b34fb";
              if (service.Uuid.ToString() == myServiceUuid)
              {
                 Get_Characteriisics(service);
                 break;
              }
           }
        }
     }
  }
  private async void Get_Characteriisics(GattDeviceService myService)
  {
     var CharResult = await myService.GetCharacteristicsAsync();
     if (CharResult.Status == GattCommunicationStatus.Success)
     {
        foreach (GattCharacteristic c in CharResult.Characteristics)
        {
           if (c.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Notify))
           {
              selectedCharacteristic = c;
              break;
           }
        }
        try
        {
           // Write the ClientCharacteristicConfigurationDescriptor in order for server to send notifications.               
           var result = await selectedCharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(
                                                     GattClientCharacteristicConfigurationDescriptorValue.Notify);
           if (result == GattCommunicationStatus.Success)
           {
              var dialogNotifications = new MessageDialog("Successfully registered for notifications");
              await dialogNotifications.ShowAsync();
              selectedCharacteristic.ValueChanged += SelectedCharacteristic_ValueChanged;
           }
           else
           {
              var dialogNotifications = new MessageDialog($"Error registering for notifications: {result}");
              await dialogNotifications.ShowAsync();
           }
        }
        catch (Exception ex)
        {
           // This usually happens when not all characteristics are found
           // or selected characteristic has no Notify.
           var dialogNotifications = new MessageDialog(ex.Message);
           await dialogNotifications.ShowAsync();
           await Task.Delay(100);
           Get_Characteriisics(myService); //try again
           //!!! Add a max try counter to prevent infinite loop!!!!!!!
        }
     }
     else
     {
        var dialogNotifications = new MessageDialog("Restricted service. Can't read characteristics");
        await dialogNotifications.ShowAsync();
     }
  }

  private void SelectedCharacteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)
  {

  }
 }
}

If you have problems with this code feel free to ask in comments.

查看更多
登录 后发表回答