BLE(心率)读数到文件

已加入
五月27,2019
留言内容
12
编程经验
1-3
你好,
我是新来的!
在构建Windows应用程序以读取Polar H10 HR测量带时需要帮助。
我从使用此代码开始; 蓝牙通用属性配置文件-心率服务.zip
示例应用程序读取HR并将值显示为条形图和列表框的窗口。
我需要将读数导出到TXT文件或什至虚拟com端口。
无论如何,即使创建/添加文本文件也没有成功 :D
我对C#的了解非常低...
我的代码如下。有人可以帮忙吗?
所有和任何想法将不胜感激。
谢谢!

异步void Instance_ValueChangeCompleted:
private async void Instance_ValueChangeCompleted(HeartRateMeasurement heartRateMeasurementValue)

        {

            // Serialize UI update to the the main UI thread.

            await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>

            {

                statusTextBlock.Text = "Latest received heart rate measurement: " +

                    heartRateMeasurementValue.HeartRateValue;


#if WINDOWS_APP

                outputDataChart.PlotChart(HeartRateService.Instance.DataPoints);

#endif


                outputListBox.Items.Insert(0, heartRateMeasurementValue);



                string path = @"C:\Users\eliseu.santos\Documents\file.txt";


                // convert string to stream

                byte[] byteArray = Encoding.UTF8.GetBytes(path);

                MemoryStream stream = new MemoryStream(byteArray);


                using (TextWriter tw = new StreamWriter(stream))

                {

                    tw.WriteLine("The next line!");

                    tw.WriteLine(heartRateMeasurementValue.HeartRateValue);

                    //tw.Dispose();

                }


            });

        }
 
Last edited:
已加入
五月27,2019
留言内容
12
编程经验
1-3
嗨跳伞者,非常感谢您的帮助。

我尝试了各种各样的示例,但总是出错:CS1503参数1:无法从“字符串”转换为“ System.IO.Stream”

C#:
private async void Instance_ValueChangeCompleted(HeartRateMeasurement heartRateMeasurementValue)
        {
            // Serialize UI update to the the main UI thread.
            await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                statusTextBlock.Text = "Latest received heart rate measurement: " +
                    heartRateMeasurementValue.HeartRateValue;

#if WINDOWS_APP
                outputDataChart.PlotChart(HeartRateService.Instance.DataPoints);
#endif

                outputListBox.Items.Insert(0, heartRateMeasurementValue);




                string docpath = @"C:\Users\eliseu.santos\Documents\";

                

                // Append text to an existing file named "WriteLines.txt".
                using (StreamWriter outputFile = new StreamWriter(Path.Combine(docpath, "file.txt")))
                {
                    outputFile.WriteLine("The next line!");
                    outputFile.WriteLine(heartRateMeasurementValue.HeartRateValue);
                }


            });
        }

您可以在这里帮助解决此错误吗?

在第一篇文章的上面的代码中没有显示任何错误,但是它不会创建任何文件,也不会写入现有文件。
所有代码示例均来自GitHub, 这里.

谢谢你。
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,979
地点
英国
编程经验
10+
您不应该手动编写路径,这就是 环境特殊文件夹 是为了。您会注意到此行在:: sWriter.WriteLine(arrText); -您可以删除环路,仅在需要时才写入心率测量值。代码已经过测试并且可以正常工作,可以根据自己的喜好进行编辑。如果您遇到困难或有任何疑问,请发回。代码具有内联注释,但是很容易说明。

Write a text file:
using System;
using System.IO;
using System.Windows.Forms;

namespace TestCSharpApp
{
    public partial class Form1 : Form
    {
        private readonly string pathOfAddress = Environment.GetFolderPath(环境特殊文件夹.MyDocuments); //Use special folders instead of manually writing the file path
        private readonly string fileName = "VisualStudioFile";
        private readonly string extAttribute = ".txt";
        private readonly string[] arrOfstuff = { "Sheepings wrote this for me", "Because my dog eat my homework" };

        public Form1()
        {
            InitializeComponent();
            var fullPath = Path.Combine(pathOfAddress, string.Concat(fileName, extAttribute)); //Combine the path and concatenate the path with its file extension
            runArgs(fullPath, arrOfstuff); //Call runArgs by passing in our path and variables to write
        }

        private void runArgs(string path, string[] arrText) //This method takes two parameters, which are fullPath, and arrOfStuff
        {
            try
            {
                using (StreamWriter sWriter = new StreamWriter(path)) //Use using blocks as they are self disposing as well as the resources used within. Pass in your path to the writer
                {
                    for (int i = 0; i < 2; i++) //Loop the strings in arrText
                    {
                        sWriter.WriteLine(arrText[i]);
                    }
                }
            }
            catch (Exception x)
            {
                //Handle any errors if any
            }
            finally
            {
                //Finnish up any work here
            }
        }
    }
}

您甚至都不需要串流编写器,可以使用 文件写所有行。希望您觉得这有用。

编辑::修正错字
 
Last edited:
已加入
五月27,2019
留言内容
12
编程经验
1-3
嗨道林斯,谢谢您的帮助。

我应该如何使用该代码?
尝试使用该代码制作新的类文件,但出现错误...
特别是这个;
Environment.GetFolderPath,给我错误CS0117“环境”不包含“ GetFolderPath”的定义

我对C#非常基础...在这里可能做错了什么。

甚至尝试部分插入该异步Void中使用该代码;

(得到几乎相同的错误...)

C#:
private async void Instance_ValueChangeCompleted(HeartRateMeasurement heartRateMeasurementValue)
        {
            // Serialize UI update to the the main UI thread.
            await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                statusTextBlock.Text = "Latest received heart rate measurement: " +
                    heartRateMeasurementValue.HeartRateValue;

#if WINDOWS_APP
                outputDataChart.PlotChart(HeartRateService.Instance.DataPoints);
#endif

                outputListBox.Items.Insert(0, heartRateMeasurementValue);
            });
        }

我只需要该课程将心律测量值放入文本文件即可。...该死,这应该很容易。 :D
 
Last edited:

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,605
地点
弗吉尼亚州切萨皮克
编程经验
10+
原因是因为您编写的示例代码是针对Windows运行时的,而Sheepings呈现的代码却是针对Windows窗体的。
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,979
地点
英国
编程经验
10+
嗯,你说:: 在构建Windows应用程序时需要帮助 -你能澄清一下吗?这是用于UWP / Windows Store吗?要不然是啥?我以为是台式机应用程序,很抱歉我应该问。

无关@菲利普·桑托斯,是的,您仍然可以使用我的代码,对其进行调整以满足您的需求。如果您要构建商店应用程序或跨平台应用程序,则将无法使用标准桌面环境应用程序中的某些功能,例如winforms。
 
已加入
五月27,2019
留言内容
12
编程经验
1-3
哦,是的,我也很抱歉缺乏澄清。
我确实需要一个独立的桌面应用程序。
我在寻找BLE HR阅读器时尝试了许多网站上的大量代码示例,到目前为止,该示例是我对编码的一点了解(除VB之外)都能成功工作并看到一些结果的唯一示例。 :D

无论如何,都要感谢双方,因为他们分享了您的代码/帮助,但是现在看来我的处境更加艰​​难了,对吧?
那么,现在,我有什么机会将这些读数导出到文本文件或COM端口,或者以其他任何方式从正在VB6中运行的其他应用程序中获取它们呢?
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,605
地点
弗吉尼亚州切萨皮克
编程经验
10+
如果您的主应用程序在VB6中?为什么不简单地向您的应用程序添加代码以从大多数蓝牙设备公开的虚拟comm端口读取?如果您已经在修改VB6程序以从某个文本文件中读取文件,为什么不直接进入源代码呢?

我隐约记得,要在VB6中从蓝牙读取数据,您需要MSCOMM.OCX,但这确实是很久以前的事了,我尽了最大努力来刷新我对VB6的所有记忆。
 
已加入
五月27,2019
留言内容
12
编程经验
1-3
:D
嘿跳伞者,希望我能做到...刷新我对VB6的记忆。但是没有太多时间去深入学习其他语言和新的开发技术/语法/等。
无论如何,AFAIK,BLE设备不会建立虚拟通信端口,这将是这里的另一个大愿望。但是会证实那些机会。
现在,回到我的老路,别介意运行第二个应用程序来收集这些HR测量值。
它有什么机会起作用?
 
已加入
五月27,2019
留言内容
12
编程经验
1-3
嗨,跳伞者,非常感谢。
现在开发平台的选择太多了,我迷上了它们。
需要了解一下... :D
我不能在那个论坛上发帖。如此多的规则和对新手的限制。
无论如何,我正在考虑使用到目前为止收集的所有信息从头开始构建桌面表单应用程序。
也许会在学习如何在此处编码的同时将主要应用程序从VB6迁移到C#。
根据您的经验,您认为最终可以工作(意味着BLE-心率测量读数)吗?
 
Last edited:

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,605
地点
弗吉尼亚州切萨皮克
编程经验
10+
对的,这是可能的。演示应用程序提供了Microsoft的事实表明,可以从设备中获取数据。

主要问题是您只有两条明显的路线可以获取数据。选项1是编写UWP应用程序的最简单方法,因为UWP框架很好地公开了BLE API。选项2处于中等难度,即编写C ++ COM应用程序,因为BLE API也作为Windows SDK / DDK的一部分由Windows公开。

不幸的是,我还没有找到将BLE API很好地包装在常规.NET Framework应用程序中的中间立场。因此,我上面的链接有关如何从常规.NET Framework应用程序访问UWP BLE API。

可以想象,由于BLE API作为COM对象公开,因此有可能创建一个Interop程序集以从.NET Framework访问它们和/或使用P / Invoke。我现在真的没有时间或倾向去探索它,但是也许有人已经做到了。只需要正确的关键词进行搜索。
 
已加入
五月27,2019
留言内容
12
编程经验
1-3
嗨跳伞者,谢谢您的投入。
因此,如果我正确理解,最好的方法就是我在第一篇文章中发布的方法,对吗?
关于您发布的链接,该控制台选项似乎很有希望...我按其地址和名称列出了我的设备,但很难获取其他任何内容,这主要是因为缺乏提取HR值所需的知识。
 
已加入
五月27,2019
留言内容
12
编程经验
1-3
再一次问好,

我的最新尝试...
Console Writing:
private void OnAdvertisementReceived(BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs eventArgs)
        {
            var HRm = new BluetoothGattHeartRate.HeartRateMeasurement();
            var HRmeasurement = HRm.HeartRateValue;

            if (eventArgs.Advertisement.LocalName == "Polar H10 1B80EF25")
            {
                // Tell the user we see an advertisement and print some properties
                Console.WriteLine(String.Format("Advertisement:"));
                Console.WriteLine(String.Format("  BT_ADDR: {0}", eventArgs.BluetoothAddress));
                Console.WriteLine(String.Format("  FR_NAME: {0}", eventArgs.Advertisement.LocalName));
                Console.WriteLine(String.Format("  TEST: {0}", HRmeasurement));
                Console.WriteLine();
            }
        }

HRmeasurment变量来自附件文件(项目添加文件)中的代码;
HRmeasurements的读数始终为0 ...
有任何想法吗?

谢谢。
菲利普·桑托斯

. Untitled.png
 

附件

  • HeartRateService.zip
    3.3 KB · Views: 64

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,605
地点
弗吉尼亚州切萨皮克
编程经验
10+
除非该代码段第3行的构造函数调用不仅仅构造一个空对象,否则您为什么期望除0以外的任何值?换句话说,您知道该构造函数是否实际上从蓝牙数据流中读取数据吗?
 
已加入
五月27,2019
留言内容
12
编程经验
1-3
是的,跳伞者,明白你的意思。
我对C#的了解正在杀死我... :D
该“心率服务”文件有2个公共类,但我认为这会给我HR值;"HeartRateService",不能从程序中调用。错误说它受到保护,我不知道为什么。
您可以帮忙使用我程序中的该文件,并设法在控制台中查看HR值吗?
谢谢你。

菲利普·桑托斯(Filipe Santos)。
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,979
地点
英国
编程经验
10+
确切的错误是什么?如果您将相关代码张贴在据说可以从心率测量值中读取的信息中,则可能也会有所帮助。

请把您的代码发布在代码标签中,而不是作为附件发布,因为人们不太可能去下载它们。我为您包括了它们。
C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Storage.Streams;

using Windows.Devices.Bluetooth.GenericAttributeProfile;
using Windows.Devices.Enumeration;
using Windows.Devices.Enumeration.Pnp;

namespace BluetoothGattHeartRate
{
    public class HeartRateMeasurement
    {
        public ushort HeartRateValue { get; set; }
        public bool HasExpendedEnergy { get; set; }
        public ushort ExpendedEnergy { get; set; }
        public DateTimeOffset Timestamp { get; set; }

        public override string ToString()
        {
            return HeartRateValue.ToString() + " bpm @ " + Timestamp.ToString();
        }
    }

    public delegate void ValueChangeCompletedHandler(HeartRateMeasurement heartRateMeasurementValue);

    public delegate void DeviceConnectionUpdatedHandler(bool isConnected);

    public class HeartRateService
    {
        // Heart Rate Constants

        // The Characteristic we want to obtain measurements for is the Heart Rate Measurement characteristic
        private Guid CHARACTERISTIC_UUID = GattCharacteristicUuids.HeartRateMeasurement;
        // Heart Rate devices typically have only one Heart Rate Measurement characteristic.
        // Make sure to check your device's documentation to find out how many characteristics your specific device has.
        private const int CHARACTERISTIC_INDEX = 0;
        // The Heart Rate Profile specification requires that the Heart Rate Measurement characteristic is notifiable.
        private const GattClientCharacteristicConfigurationDescriptorValue CHARACTERISTIC_NOTIFICATION_TYPE =
            GattClientCharacteristicConfigurationDescriptorValue.Notify;

        // A pointer back to the main page.  This is needed if you want to call methods in MainPage such
        // as NotifyUser().
        //MainPage rootPage = MainPage.Current;

        private static readonly HeartRateService instance = new HeartRateService();
        private GattDeviceService service;
        private GattCharacteristic characteristic;
        private List<HeartRateMeasurement> datapoints;
        private PnpObjectWatcher watcher;
        private String deviceContainerId;

        public event ValueChangeCompletedHandler ValueChangeCompleted;
        public event DeviceConnectionUpdatedHandler DeviceConnectionUpdated;

        public static HeartRateService Instance
        {
            get { return instance; }
        }

        public bool IsServiceInitialized { get; set; }

        public GattDeviceService Service
        {
            get { return service; }
        }

        public HeartRateMeasurement[] DataPoints
        {
            get
            {
                HeartRateMeasurement[] retval;
                lock (datapoints)
                {
                    retval = datapoints.ToArray();
                }

                return retval;
            }
        }

        private HeartRateService()
        {
            datapoints = new List<HeartRateMeasurement>();
            //App.Current.Suspending += App_Suspending;
            //App.Current.Resuming += App_Resuming;
        }

        private void App_Resuming(object sender, object e)
        {
            // Since the Windows Runtime will close resources to the device when the app is suspended,
            // the device needs to be reinitialized when the app is resumed.
        }

        private void App_Suspending(object sender, Windows.ApplicationModel.SuspendingEventArgs e)
        {
            IsServiceInitialized = false;

            // This is an appropriate place to save to persistent storage any datapoint the application cares about.
            // For the purpose of this sample we just discard any values.
            datapoints.Clear();

            // Allow the GattDeviceService to get cleaned up by the Windows Runtime.
            // The Windows runtime will clean up resources used by the GattDeviceService object when the application is
            // suspended. The GattDeviceService object will be invalid once the app resumes, which is why it must be
            // marked as invalid, and reinitalized when the application resumes.
            if (service != null)
            {
                service.Dispose();
                service = null;
            }

            if (characteristic != null)
            {
                characteristic = null;
            }

            if (watcher != null)
            {
                watcher.Stop();
                watcher = null;
            }
        }

        public async Task InitializeServiceAsync(DeviceInformation device)
        {
            try
            {
                deviceContainerId = "{" + device.Properties["System.Devices.ContainerId"] + "}";

                service = await GattDeviceService.FromIdAsync(device.Id);
                if (service != null)
                {
                    IsServiceInitialized = true;
                    await ConfigureServiceForNotificationsAsync();
                }
            }
            catch (Exception e)
            {
            }
        }
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,979
地点
英国
编程经验
10+
C#:
        /// <summary>

        /// Configure the Bluetooth device to send notifications whenever the Characteristic value changes

        /// </summary>

        private async Task ConfigureServiceForNotificationsAsync()

        {

            try

            {

                // Obtain the characteristic for which notifications are to be received

                characteristic = service.GetCharacteristics(CHARACTERISTIC_UUID)[CHARACTERISTIC_INDEX];



                // While encryption is not required by all devices, if encryption is supported by the device,

                // it can be enabled by setting the ProtectionLevel property of the Characteristic object.

                // All subsequent operations on the characteristic will work over an encrypted link.

                characteristic.ProtectionLevel = GattProtectionLevel.EncryptionRequired;



                // Register the event handler for receiving notifications

                characteristic.ValueChanged += Characteristic_ValueChanged;



                // In order to avoid unnecessary communication with the device, determine if the device is already

                // correctly configured to send notifications.

                // By default ReadClientCharacteristicConfigurationDescriptorAsync will attempt to get the current

                // value from the system cache and communication with the device is not typically required.

                var currentDescriptorValue = await characteristic.ReadClientCharacteristicConfigurationDescriptorAsync();



                if ((currentDescriptorValue.Status != GattCommunicationStatus.Success) ||

                    (currentDescriptorValue.ClientCharacteristicConfigurationDescriptor != CHARACTERISTIC_NOTIFICATION_TYPE))

                {

                    // Set the Client Characteristic Configuration Descriptor to enable the device to send notifications

                    // when the Characteristic value changes

                    GattCommunicationStatus status =

                        await characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(

                        CHARACTERISTIC_NOTIFICATION_TYPE);



                    if (status == GattCommunicationStatus.Unreachable)

                    {

                        // Register a PnpObjectWatcher to detect when a connection to the device is established,

                        // such that the application can retry device configuration.

                        StartDeviceConnectionWatcher();

                    }

                }

            }

            catch (Exception e)

            {

            }

        }



        /// <summary>

        /// Register to be notified when a connection is established to the Bluetooth device

        /// </summary>

        private void StartDeviceConnectionWatcher()

        {

            watcher = PnpObject.CreateWatcher(PnpObjectType.DeviceContainer,

                new string[] { "System.Devices.Connected" }, String.Empty);



            watcher.Updated += DeviceConnection_Updated;

            watcher.Start();

        }



        /// <summary>

        /// Invoked when a connection is established to the Bluetooth device

        /// </summary>

        /// <param name="sender">The watcher object that sent the notification</param>

        /// <param name="args">The updated device object properties</param>

        private async void DeviceConnection_Updated(PnpObjectWatcher sender, PnpObjectUpdate args)

        {

            var connectedProperty = args.Properties["System.Devices.Connected"];

            bool isConnected = false;

            if ((deviceContainerId == args.Id) && Boolean.TryParse(connectedProperty.ToString(), out isConnected) &&

                isConnected)

            {

                var status = await characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(

                    CHARACTERISTIC_NOTIFICATION_TYPE);



                if (status == GattCommunicationStatus.Success)

                {

                    IsServiceInitialized = true;



                    // Once the Client Characteristic Configuration Descriptor is set, the watcher is no longer required

                    watcher.Stop();

                    watcher = null;

                }



                // Notifying subscribers of connection state updates

                if (DeviceConnectionUpdated != null)

                {

                    DeviceConnectionUpdated(isConnected);

                }

            }

        }



        /// <summary>

        /// Invoked when Windows receives data from your Bluetooth device.

        /// </summary>

        /// <param name="sender">The GattCharacteristic object whose value is received.</param>

        /// <param name="args">The new characteristic value sent by the device.</param>

        private void Characteristic_ValueChanged(

            GattCharacteristic sender,

            GattValueChangedEventArgs args)

        {

            var data = new byte[args.CharacteristicValue.Length];



            DataReader.FromBuffer(args.CharacteristicValue).ReadBytes(data);



            // Process the raw data received from the device.

            var value = ProcessData(data);

            value.Timestamp = args.Timestamp;



            lock (datapoints)

            {

                datapoints.Add(value);

            }



            if (ValueChangeCompleted != null)

            {

                ValueChangeCompleted(value);

            }

        }



        /// <summary>

        /// Process the raw data received from the device into application usable data,

        /// according the the Bluetooth Heart Rate Profile.

        /// </summary>

        /// <param name="data">Raw data received from the heart rate monitor.</param>

        /// <returns>The heart rate measurement value.</returns>

        private HeartRateMeasurement ProcessData(byte[] data)

        {

            // Heart Rate profile defined flag values

            const byte HEART_RATE_VALUE_FORMAT = 0x01;

            const byte ENERGY_EXPANDED_STATUS = 0x08;



            byte currentOffset = 0;

            byte flags = data[currentOffset];

            bool isHeartRateValueSizeLong = ((flags & HEART_RATE_VALUE_FORMAT) != 0);

            bool hasEnergyExpended = ((flags & ENERGY_EXPANDED_STATUS) != 0);



            currentOffset++;



            ushort heartRateMeasurementValue = 0;

            if (isHeartRateValueSizeLong)

            {

                heartRateMeasurementValue = (ushort)((data[currentOffset + 1] << 8) + data[currentOffset]);

                currentOffset += 2;

            }

            else

            {

                heartRateMeasurementValue = data[currentOffset];

                currentOffset++;

            }



            // The Heart Rate Bluetooth profile can also contain sensor contact status information,

            // and R-Wave interval measurements, which can also be processed here.

            // For the purpose of this sample, we don't need to interpret that data.



            return new HeartRateMeasurement

            {

                HeartRateValue = heartRateMeasurementValue,

            };

        }

    }

}
由于1000个发布限制,我不得不分割文件。
 
最佳 底部