Stream data from UWP to TCP server c# (Windows BLE Example)

0

I have a BLE code in c#. And I have a TCP Client code as well, which is in a new project. However, I am looking for a way to add the client code in an existing BLE code to get the data on TCP server. I have a heart rate monitor which is connected with Bluetooth. I want to get those data (marked in red) on the TCP server. Besides adding libraries where should I add the TCP Client code in BLE? Any help is appreciated. Thanks

  (BLE code is basically a sample from GitHub: 
   Link:: https://github.com/Microsoft/Windows-universal- 
          samples/tree/master/Samples/BluetoothLE/cs


  image: https://ibb.co/m2tqrp


                         BLE CODE:        

       namespace SDKTemplate
      {
// This scenario connects to the device selected in the "Discover
// GATT Servers" scenario and communicates with it.
// Note that this scenario is rather artificial because it communicates
// with an unknown service with unknown characteristics.
// In practice, your app will be interested in a specific service with
// a specific characteristic.
public sealed partial class Scenario2_Client : Page
{
    private MainPage rootPage = MainPage.Current;

    private ObservableCollection<BluetoothLEAttributeDisplay> 
  ServiceCollection = new ObservableCollection<BluetoothLEAttributeDisplay> 
  ();
    private ObservableCollection<BluetoothLEAttributeDisplay> 
    CharacteristicCollection = new 
    ObservableCollection<BluetoothLEAttributeDisplay>();

    private BluetoothLEDevice bluetoothLeDevice = null;
    private GattCharacteristic selectedCharacteristic;

    // Only one registered characteristic at a time.
    private GattCharacteristic registeredCharacteristic;
    private GattPresentationFormat presentationFormat;

    #region Error Codes
    readonly int E_BLUETOOTH_ATT_WRITE_NOT_PERMITTED = 
    unchecked((int)0x80650003);
    readonly int E_BLUETOOTH_ATT_INVALID_PDU = unchecked((int)0x80650004);
    readonly int E_ACCESSDENIED = unchecked((int)0x80070005);
    readonly int E_DEVICE_NOT_AVAILABLE = unchecked((int)0x800710df); // 
    HRESULT_FROM_WIN32(ERROR_DEVICE_NOT_AVAILABLE)
    #endregion

    #region UI Code
    public Scenario2_Client()
    {
        InitializeComponent();
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        if (string.IsNullOrEmpty(rootPage.SelectedBleDeviceId))
        {
            ConnectButton.IsEnabled = false;
        }
    }

    protected override async void OnNavigatedFrom(NavigationEventArgs e)
    {
        var success = await ClearBluetoothLEDeviceAsync();
        if (!success)
        {
            rootPage.NotifyUser("Error: Unable to reset app state", 
      NotifyType.ErrorMessage);
        }
    }
    #endregion

    #region Enumerating Services
    private async Task<bool> ClearBluetoothLEDeviceAsync()
    {
        if (subscribedForNotifications)
        {
            // Need to clear the CCCD from the remote device so we stop 
            receiving notifications
            var result = awai registeredCharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.None);
            if (result != GattCommunicationStatus.Success)
            {
                return false;
            }
            else
            {
                selectedCharacteristic.ValueChanged -= 
              Characteristic_ValueChanged;
                subscribedForNotifications = false;
            }
        }
        bluetoothLeDevice?.Dispose();
        bluetoothLeDevice = null;
        return true;
    }

    private async void ConnectButton_Click()
    {
        ConnectButton.IsEnabled = false;

        if (!await ClearBluetoothLEDeviceAsync())
        {
            rootPage.NotifyUser("Error: Unable to reset state, try again.", NotifyType.ErrorMessage);
            ConnectButton.IsEnabled = false;
            return;
        }

        try
        {
            // BT_Code: BluetoothLEDevice.FromIdAsync must be called from a UI thread because it may prompt for consent.
            bluetoothLeDevice = await BluetoothLEDevice.FromIdAsync(rootPage.SelectedBleDeviceId);

            if (bluetoothLeDevice == null)
            {
                rootPage.NotifyUser("Failed to connect to device.", NotifyType.ErrorMessage);
            }
        }
        catch (Exception ex) when (ex.HResult == E_DEVICE_NOT_AVAILABLE)
        {
            rootPage.NotifyUser("Bluetooth radio is not on.", NotifyType.ErrorMessage);
        }

        if (bluetoothLeDevice != null)
        {
            // Note: BluetoothLEDevice.GattServices property will return an 
            empty list for unpaired devices. For all uses we recommend using 
            the GetGattServicesAsync method.
            // BT_Code: GetGattServicesAsync returns a list of all the 
             supported services of the device (even if it's not paired to the 
             system).
            // If the services supported by the device are expected to change 
            during BT usage, subscribe to the GattServicesChanged event.
            GattDeviceServicesResult result = await 


         bluetoothLeDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached);

            if (result.Status == GattCommunicationStatus.Success)
            {
                var services = result.Services;
                rootPage.NotifyUser(String.Format("Found {0} services", 
               services.Count), NotifyType.StatusMessage);
                foreach (var service in services)
                {
                    ServiceCollection.Add(new 
          BluetoothLEAttributeDisplay(service));
                }
                ConnectButton.Visibility = Visibility.Collapsed;
                ServiceList.Visibility = Visibility.Visible;
            }
            else
            {
                rootPage.NotifyUser("Device unreachable", 
              NotifyType.ErrorMessage);
            }
        }
        ConnectButton.IsEnabled = true;
    }
    #endregion

    #region Enumerating Characteristics
    private async void ServiceList_SelectionChanged()
    {
        var attributeInfoDisp = (BluetoothLEAttributeDisplay)ServiceList.SelectedItem;

        CharacteristicCollection.Clear();
        RemoveValueChangedHandler();

        IReadOnlyList<GattCharacteristic> characteristics = null;
        try
        {
            // Ensure we have access to the device.
            var accessStatus = await attributeInfoDisp.service.RequestAccessAsync();
            if (accessStatus == DeviceAccessStatus.Allowed)
            {
                // BT_Code: Get all the child characteristics of a service. Use the cache mode to specify uncached characterstics only 
                // and the new Async functions to get the characteristics of unpaired devices as well. 
                var result = await attributeInfoDisp.service.GetCharacteristicsAsync(BluetoothCacheMode.Uncached);
                if (result.Status == GattCommunicationStatus.Success)
                {
                    characteristics = result.Characteristics;
                }
                else
                {
                    rootPage.NotifyUser("Error accessing service.", NotifyType.ErrorMessage);

                    // On error, act as if there are no characteristics.
                    characteristics = new List<GattCharacteristic>();
                }
            }
            else
            {
                // Not granted access
                rootPage.NotifyUser("Error accessing service.", NotifyType.ErrorMessage);

                // On error, act as if there are no characteristics.
                characteristics = new List<GattCharacteristic>();

            }
        }
        catch (Exception ex)
        {
            rootPage.NotifyUser("Restricted service. Can't read characteristics: " + ex.Message,
                NotifyType.ErrorMessage);
            // On error, act as if there are no characteristics.
            characteristics = new List<GattCharacteristic>();
        }

        foreach (GattCharacteristic c in characteristics)
        {
            CharacteristicCollection.Add(new BluetoothLEAttributeDisplay(c));
        }
        CharacteristicList.Visibility = Visibility.Visible;
    }
    #endregion

    private void AddValueChangedHandler()
    {
        ValueChangedSubscribeToggle.Content = "Unsubscribe from value changes";
        if (!subscribedForNotifications)
        {
            registeredCharacteristic = selectedCharacteristic;
            registeredCharacteristic.ValueChanged += Characteristic_ValueChanged;
            subscribedForNotifications = true;
        }
    }

    private void RemoveValueChangedHandler()
    {
        ValueChangedSubscribeToggle.Content = "Subscribe to value changes";
        if (subscribedForNotifications)
        {
            registeredCharacteristic.ValueChanged -= Characteristic_ValueChanged;
            registeredCharacteristic = null;
            subscribedForNotifications = false;
        }
    }

    private async void CharacteristicList_SelectionChanged()
    {
        selectedCharacteristic = null;

        var attributeInfoDisp = (BluetoothLEAttributeDisplay)CharacteristicList.SelectedItem;
        if (attributeInfoDisp == null)
        {
            EnableCharacteristicPanels(GattCharacteristicProperties.None);
            return;
        }

        selectedCharacteristic = attributeInfoDisp.characteristic;
        if (selectedCharacteristic == null)
        {
            rootPage.NotifyUser("No characteristic selected", NotifyType.ErrorMessage);
            return;
        }

        // Get all the child descriptors of a characteristics. Use the cache mode to specify uncached descriptors only 
        // and the new Async functions to get the descriptors of unpaired devices as well. 
        var result = await selectedCharacteristic.GetDescriptorsAsync(BluetoothCacheMode.Uncached);
        if (result.Status != GattCommunicationStatus.Success)
        {
            rootPage.NotifyUser("Descriptor read failure: " + result.Status.ToString(), NotifyType.ErrorMessage);
        }

        // BT_Code: There's no need to access presentation format unless there's at least one. 
        presentationFormat = null;
        if (selectedCharacteristic.PresentationFormats.Count > 0)
        {

            if (selectedCharacteristic.PresentationFormats.Count.Equals(1))
            {
                // Get the presentation format since there's only one way of presenting it
                presentationFormat = selectedCharacteristic.PresentationFormats[0];
            }
            else
            {
                // It's difficult to figure out how to split up a characteristic and encode its different parts properly.
                // In this case, we'll just encode the whole thing to a string to make it easy to print out.
            }
        }

        // Enable/disable operations based on the GattCharacteristicProperties.
        EnableCharacteristicPanels(selectedCharacteristic.CharacteristicProperties);
    }

    private void SetVisibility(UIElement element, bool visible)
    {
        element.Visibility = visible ? Visibility.Visible : Visibility.Collapsed;
    }

    private void EnableCharacteristicPanels(GattCharacteristicProperties properties)
    {
        // BT_Code: Hide the controls which do not apply to this characteristic.
        SetVisibility(CharacteristicReadButton, properties.HasFlag(GattCharacteristicProperties.Read));

        SetVisibility(CharacteristicWritePanel,
            properties.HasFlag(GattCharacteristicProperties.Write) ||
            properties.HasFlag(GattCharacteristicProperties.WriteWithoutResponse));
        CharacteristicWriteValue.Text = "";

        SetVisibility(ValueChangedSubscribeToggle, properties.HasFlag(GattCharacteristicProperties.Indicate) ||
                                                   properties.HasFlag(GattCharacteristicProperties.Notify));

    }

    private async void CharacteristicReadButton_Click()
    {
        // BT_Code: Read the actual value from the device by using Uncached.
        GattReadResult result = await selectedCharacteristic.ReadValueAsync(BluetoothCacheMode.Uncached);
        if (result.Status == GattCommunicationStatus.Success)
        {
            string formattedResult = FormatValueByPresentation(result.Value, presentationFormat);
            rootPage.NotifyUser($"Read result: {formattedResult}", NotifyType.StatusMessage);
        }
        else
        {
            rootPage.NotifyUser($"Read failed: {result.Status}", NotifyType.ErrorMessage);
        }
    }

    private async void CharacteristicWriteButton_Click()
    {
        if (!String.IsNullOrEmpty(CharacteristicWriteValue.Text))
        {
            var writeBuffer = CryptographicBuffer.ConvertStringToBinary(CharacteristicWriteValue.Text,
                BinaryStringEncoding.Utf8);

            var writeSuccessful = await WriteBufferToSelectedCharacteristicAsync(writeBuffer);
        }
        else
        {
            rootPage.NotifyUser("No data to write to device", NotifyType.ErrorMessage);
        }
    }

    private async void CharacteristicWriteButtonInt_Click()
    {
        if (!String.IsNullOrEmpty(CharacteristicWriteValue.Text))
        {
            var isValidValue = Int32.TryParse(CharacteristicWriteValue.Text, out int readValue);
            if (isValidValue)
            {
                var writer = new DataWriter();
                writer.ByteOrder = ByteOrder.LittleEndian;
                writer.WriteInt32(readValue);

                var writeSuccessful = await WriteBufferToSelectedCharacteristicAsync(writer.DetachBuffer());
            }
            else
            {
                rootPage.NotifyUser("Data to write has to be an int32", NotifyType.ErrorMessage);
            }
        }
        else
        {
            rootPage.NotifyUser("No data to write to device", NotifyType.ErrorMessage);
        }
    }

    private async Task<bool> WriteBufferToSelectedCharacteristicAsync(IBuffer buffer)
    {
        try
        {
            // BT_Code: Writes the value from the buffer to the characteristic.
            var result = await selectedCharacteristic.WriteValueWithResultAsync(buffer);

            if (result.Status == GattCommunicationStatus.Success)
            {
                rootPage.NotifyUser("Successfully wrote value to device", NotifyType.StatusMessage);
                return true;
            }
            else
            {
                rootPage.NotifyUser($"Write failed: {result.Status}", NotifyType.ErrorMessage);
                return false;
            }
        }
        catch (Exception ex) when (ex.HResult == E_BLUETOOTH_ATT_INVALID_PDU)
        {
            rootPage.NotifyUser(ex.Message, NotifyType.ErrorMessage);
            return false;
        }
        catch (Exception ex) when (ex.HResult == E_BLUETOOTH_ATT_WRITE_NOT_PERMITTED || ex.HResult == E_ACCESSDENIED)
        {
            // This usually happens when a device reports that it support writing, but it actually doesn't.
            rootPage.NotifyUser(ex.Message, NotifyType.ErrorMessage);
            return false;
        }
    }

    private bool subscribedForNotifications = false;
    private async void ValueChangedSubscribeToggle_Click()
    {
        if (!subscribedForNotifications)
        {
            // initialize status
            GattCommunicationStatus status = GattCommunicationStatus.Unreachable;
            var cccdValue = GattClientCharacteristicConfigurationDescriptorValue.None;
            if (selectedCharacteristic.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Indicate))
            {
                cccdValue = GattClientCharacteristicConfigurationDescriptorValue.Indicate;
            }

            else if (selectedCharacteristic.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Notify))
            {
                cccdValue = GattClientCharacteristicConfigurationDescriptorValue.Notify;
            }

            try
            {
                // BT_Code: Must write the CCCD in order for server to send indications.
                // We receive them in the ValueChanged event handler.
                status = await selectedCharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(cccdValue);

                if (status == GattCommunicationStatus.Success)
                {
                    AddValueChangedHandler();
                    rootPage.NotifyUser("Successfully subscribed for value changes", NotifyType.StatusMessage);
                }
                else
                {
                    rootPage.NotifyUser($"Error registering for value changes: {status}", NotifyType.ErrorMessage);
                }
            }
            catch (UnauthorizedAccessException ex)
            {
                // This usually happens when a device reports that it support indicate, but it actually doesn't.
                rootPage.NotifyUser(ex.Message, NotifyType.ErrorMessage);
            }
        }
        else
        {
            try
            {
                // BT_Code: Must write the CCCD in order for server to send notifications.
                // We receive them in the ValueChanged event handler.
                // Note that this sample configures either Indicate or Notify, but not both.
                var result = await
                        selectedCharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(
                            GattClientCharacteristicConfigurationDescriptorValue.None);
                if (result == GattCommunicationStatus.Success)
                {
                    subscribedForNotifications = false;
                    RemoveValueChangedHandler();
                    rootPage.NotifyUser("Successfully un-registered for notifications", NotifyType.StatusMessage);
                    //GattServiceUuids.Battery
                }
                else
                {
                    rootPage.NotifyUser($"Error un-registering for notifications: {result}", NotifyType.ErrorMessage);
                }
            }
            catch (UnauthorizedAccessException ex)
            {
                // This usually happens when a device reports that it support notify, but it actually doesn't.
                rootPage.NotifyUser(ex.Message, NotifyType.ErrorMessage);
            }
        }
    }

    private async void Characteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)
    {
        // BT_Code: An Indicate or Notify reported that the value has changed.
        // Display the new value with a timestamp.
        // sender.Uuid.Equals(GattCharacteristicUuids.HeartRateMeasurement)

        var newValue = FormatValueByPresentation(args.CharacteristicValue, presentationFormat);
        var message = $"Value at {DateTime.Now:hh:mm:ss.FFF}: {newValue}";
        await Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
            () => CharacteristicLatestValue.Text = message);
    }

    private string FormatValueByPresentation(IBuffer buffer, GattPresentationFormat format)
    {
        // BT_Code: For the purpose of this sample, this function converts only UInt32 and
        // UTF-8 buffers to readable text. It can be extended to support other formats if your app needs them.
        byte[] data;
        CryptographicBuffer.CopyToByteArray(buffer, out data);
        if (format != null)
        {
            if (format.FormatType == GattPresentationFormatTypes.UInt32 && data.Length >= 4)
            {
                return BitConverter.ToInt32(data, 0).ToString();
            }
            else if (format.FormatType == GattPresentationFormatTypes.Utf8)
            {
                try
                {
                    return Encoding.UTF8.GetString(data);
                }
                catch (ArgumentException)
                {
                    return "(error: Invalid UTF-8 string)";
                }
            }
            else
            {
                // Add support for other format types as needed.
                return "Unsupported format: " + CryptographicBuffer.EncodeToHexString(buffer);
            }
        }
        else if (data != null)
        {
            // We don't know what format to use. Let's try some well-known profiles, or default back to UTF-8.
            if (selectedCharacteristic.Uuid.Equals(GattCharacteristicUuids.HeartRateMeasurement))
            {
                try
                {
                    string b = ParseHeartRateValue(data).ToString();
                    TrySend(b);
                    return "Heart Rate: " + b;
                }
                catch (ArgumentException)
                {
                    return "Heart Rate: (unable to parse)";
                }
            }
            else if (selectedCharacteristic.Uuid.Equals(GattCharacteristicUuids.BatteryLevel))
            {
                try
                {
                    // battery level is encoded as a percentage value in the first byte according to
                    // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.battery_level.xml
                    return "Battery Level: " + data[0].ToString() + "%";
                }
                catch (ArgumentException)
                {
                    return "Battery Level: (unable to parse)";
                }
            }
            // This is our custom calc service Result UUID. Format it like an Int
            else if (selectedCharacteristic.Uuid.Equals(Constants.ResultCharacteristicUuid))
            {
                return BitConverter.ToInt32(data, 0).ToString();
            }
            // No guarantees on if a characteristic is registered for notifications.
            else if (registeredCharacteristic != null)
            {
                // This is our custom calc service Result UUID. Format it like an Int
                if (registeredCharacteristic.Uuid.Equals(Constants.ResultCharacteristicUuid))
                {
                    return BitConverter.ToInt32(data, 0).ToString();
                }
            }
            else
            {
                try
                {
                    return "Unknown format: " + Encoding.UTF8.GetString(data);
                }
                catch (ArgumentException)
                {
                    return "Unknown format";
                }
            }
        }
        else
        {
            return "Empty data received";
        }
        return "Unknown format";
    }




    private async void TrySend(string data)

    {
        try
        {
            // Create the StreamSocket and establish a connection to the echo server.
            using (var streamSocket = new Windows.Networking.Sockets.StreamSocket())
            {
                //The server hostname that we will be establishing a connection to. In this example, the server and client are in the same process.

                await streamSocket.ConnectAsync((new Windows.Networking.HostName("localhost")), "9999");

                // Send a request to the echo server.
                using (Stream outputStream = streamSocket.OutputStream.AsStreamForWrite())
                {
                    using (var streamWriter = new StreamWriter(outputStream))
                    {
                        await streamWriter.WriteLineAsync(data);
                        await streamWriter.FlushAsync();
                    }
                }
            }
        }
        catch (Exception)
        {
        }
    }

    /// <summary>
    /// Process the raw data received from the device into application usable data,
    /// according the the Bluetooth Heart Rate Profile.
    /// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml&u=org.bluetooth.characteristic.heart_rate_measurement.xml
    /// This function throws an exception if the data cannot be parsed.
    /// </summary>
    /// <param name="data">Raw data received from the heart rate monitor.</param>
    /// <returns>The heart rate measurement value.</returns>
    private static ushort ParseHeartRateValue(byte[] data)
    {
        // Heart Rate profile defined flag values
        const byte heartRateValueFormat = 0x01;

        byte flags = data[0];
        bool isHeartRateValueSizeLong = ((flags & heartRateValueFormat) != 0);

        if (isHeartRateValueSizeLong)
        {
            return BitConverter.ToUInt16(data, 1);
        }
        else
        {
            return data[1];
        }
    }
}

 }

                      TCP Client : 

           using System;
         using System.Collections.Generic;
        using System.Linq;
        using System.Text;
       using System.Threading.Tasks;
      using System.IO;
      using System.Net;
     using System.Net.Sockets;

        public class EchoClient { 

       public static void Main() { 

         try { 

        TcpClient client = new TcpClient("139.169.63.130", 9999);
        StreamReader reader = new StreamReader(client.GetStream());
        StreamWriter writer = new StreamWriter(client.GetStream());
        string s = string.Empty;
        while(!s.Equals("Exit")) {

            Console.WriteLine("TCP Client connected....");
            Console.Write("Enter a string or number to send to the server: 
            ");
            s = Console.ReadLine();
            Console.WriteLine();
            writer.WriteLine(s);
            writer.Flush();
            string server_string = reader.ReadLine();
            Console.WriteLine(server_string);
        }

        reader.Close();
        writer.Close();
        client.Close();

    } catch(Exception e) { 

        Console.WriteLine(e);

       }
     }
   }
c#
windows
tcp
bluetooth-lowenergy
tcpclient
asked on Stack Overflow Sep 27, 2018 by Smit Modi • edited Sep 28, 2018 by Smit Modi

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0