MAUI APP开发蓝牙协议的经验分享:与跳绳设备对接

news2024/12/27 4:49:59

在开发MAUI应用程序时,蓝牙协议的应用是一个重要的环节,尤其是在需要与外部设备如智能跳绳进行数据交换的场景中。以下是我在开发过程中的一些经验和心得,希望能为你的项目提供帮助。

1. 蓝牙协议基础

蓝牙协议是无线通信的一种标准,允许设备之间进行短距离的数据交换。在MAUI开发中,我们主要关注的是BLE(Bluetooth Low Energy,低功耗蓝牙)协议,它以其低功耗和低成本的特点被广泛应用于智能设备中。

2. 使用Ble蓝牙助手

在开发过程中,我使用了“BLE蓝牙助手”这款应用来辅助调试和理解蓝牙协议。这款应用可以帮助我们扫描周围的BLE设备,查看设备的信号强度(RSSI),连接设备,并查看服务和特征。通过这个工具,我们可以更直观地理解BLE协议的工作原理和数据交换过程。

3. MAUI中的蓝牙开发

        MAUI(.NET Multi-platform App UI)是一个跨平台框架,它允许开发者使用C#和XAML创建跨平台的移动和桌面应用。MAUI以其性能优异、可扩展性强和结构简单而受到开发者的青睐。在本次开发中,MAUI作为主要的开发框架,提供了丰富的控件和API,使得与蓝牙设备的对接成为可能。

        MAUI支持多种平台,包括Android、iOS、macOS和Windows,这为开发跨平台应用提供了极大的便利。在本项目中,我们将重点利用MAUI的跨平台特性,开发一款能够在不同操作系统上运行的跳绳计数应用。

        在MAUI中,我们可以通过Plugin.BLE来实现蓝牙功能。以下是一些关键步骤:

3.1 扫描设备

首先,我们需要扫描周围的BLE设备。在MAUI中,我们可以使用以下代码来启动扫描:

await CurrentAdapter.StartScanningForDevicesAsync();

public  async Task<bool> StartScanAsync()
        {
            //检查获取蓝牙权限
            bool isPermissionPass = await CheckAndRequestBluetoothPermission();
            if (!isPermissionPass)
                return false;
            // 在使用之前,确保 _scanForAedCts 已经被实例化
            

            ListDevice.Clear();

            try
            {
                if(CurrentAdapter == null)
                {
                    CurrentAdapter = CrossBluetoothLE.Current.Adapter;
                }
                await CurrentAdapter.StopScanningForDevicesAsync();

                CurrentAdapter.DeviceDiscovered += Adapter_DeviceDiscovered;
                CurrentAdapter.ScanTimeoutElapsed += Adapter_ScanTimeoutElapsed;

                //蓝牙扫描时间
                CurrentAdapter.ScanTimeout = 30 * 1000;

                //默认LowPower
                CurrentAdapter.ScanMode = Plugin.BLE.Abstractions.Contracts.ScanMode.LowPower;

                Debug.WriteLine($"开始扫描外设, IsAvailable={CrossBluetoothLE.Current.IsAvailable}, IsOn={CrossBluetoothLE.Current.IsOn}, State={CrossBluetoothLE.Current.State}, ScanMode={CurrentAdapter.ScanMode}, ScanTimeout={CurrentAdapter.ScanTimeout}");

                await CurrentAdapter.StartScanningForDevicesAsync(cancellationToken: _scanForAedCts.Token);

                Debug.WriteLine($"结束扫描外设");
            }
            catch (OperationCanceledException)
            {
                Debug.WriteLine($"扫描外设任务取消");
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"扫描外设出错, {ex.Message}");
            }
            finally
            {
                CurrentAdapter.DeviceDiscovered -= Adapter_DeviceDiscovered;
                CurrentAdapter.ScanTimeoutElapsed -= Adapter_ScanTimeoutElapsed;
                //_scanForAedCts.Dispose();
            }

            return true;
        }

在扫描过程中,我们可以通过DeviceDiscovered事件来获取发现的设备信息。

3.2 连接设备

一旦找到目标设备,我们就可以建立连接。在MAUI中,连接设备的过程如下:

await CurrentAdapter.ConnectToDeviceAsync(device, new ConnectParameters(false, true));

    /// <summary>
        /// 连接设备
        /// </summary>
        /// <param name="uuid"></param>
        /// <returns></returns>
        public async Task<IDevice?> ConnectDeviceAsync(Guid uuid)
        {
            try
            {
                if (CurrentAdapter == null)
                {
                    CurrentAdapter = CrossBluetoothLE.Current.Adapter;
                }

                var connectedDevices = CurrentAdapter.ConnectedDevices;
                if (connectedDevices.Count > 0)
                {
                    // 至少有一个设备已经连接
                    foreach (var device in connectedDevices)
                    {
                        // 可以在这里处理每个已连接的设备
                        if (device.Id == uuid)
                        {
                            await StartNotify(device);
                            return device;
                        }
                        Console.WriteLine(device.Name);
                    }
                }
                else
                {
                    try
                    {
                        using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15)))
                        {
                            var device = await CurrentAdapter.ConnectToKnownDeviceAsync(uuid, default(ConnectParameters), cts.Token);
                            await StartNotify(device);
                            return device;
                        }
                    }
                    catch (Exception ex) { Debug.WriteLine($"蓝牙连接设备失败, {ex.Message}"); }
                }
                
            }
            catch(Exception ex)
            {
                Debug.WriteLine($"蓝牙连接设备失败, {ex.Message}");
            }
            return null;
        }

这里device是我们通过扫描得到的设备对象,ConnectParameters用于设置连接参数。

3.3 获取服务和特征

连接成功后,我们可以获取设备提供的服务和特征,这对于数据交换至关重要:

var services = await device.GetGattServicesAsync();

3.4 数据读写

通过获取的特征,我们可以进行数据的读写操作。例如,读取跳绳的次数和时间:

var characteristic = services.First().GetCharacteristics().First();
var readResult = await characteristic.ReadValueAsync();



/// <summary>
        /// 读取数据
        /// </summary>
        /// <param name="characteristic"></param>
        /// <returns></returns>
        public async Task<byte[]?> ReadDataAsync(ICharacteristic characteristic)
        {
            //根据Plugin.BLE要求,在主线程读写数据
            var result = await MainThread.InvokeOnMainThreadAsync(async () =>
            {
                try
                {
                    //读取数据
                    var ary = await characteristic.ReadAsync();
                    Debug.WriteLine($"读取成功,长度={ary.data.Length}");
                    return (ary.data);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine($"读取错误, 目标设备蓝牙连接状态={BleDevice?.State}, {ex.Message}");
                    return null;
                }
            });

            return result;
        }

        /// <summary>
        /// 读取蓝牙设备数据
        /// </summary>
        /// <param name="guid"></param>
        /// <returns></returns>
        public async Task<byte[]?> ReadDataAsync(IDevice device)
        {
            var service = await device.GetServiceAsync(new Guid("0000180D-0000-1000-8000-00805F9B34FB"));
            if (service != null)
            {
                var characteristic = await service.GetCharacteristicAsync(new Guid("00002A37-0000-1000-8000-00805F9B34FB"));
                if (characteristic != null)
                {
                    var ary = await characteristic.ReadAsync();
                    return (ary.data);
                }
            }
            return null;
        }


/// <summary>
        /// 写入蓝牙设备数据
        /// </summary>
        /// <param name="device"></param>
        /// <param name="ary"></param>
        /// <returns></returns>
        public async Task<int> SendDataAsync(IDevice device, byte[] ary)
        {
            var service = await device.GetServiceAsync(_serviceUuid);
            if (service != null)
            {
                var characteristic = await service.GetCharacteristicAsync(_characteristicUuid);
                if (characteristic != null && characteristic.CanWrite==true)
                {
                    return await characteristic.WriteAsync(ary);
                }
            }
            return 0;
        }

3.5事件订阅

通过获取的特征,我们可以进行数据的通知操作。

#region 订阅事件

        private async Task<int> StartNotify(IDevice device)
        {
            try
            {
                var service = await device.GetServiceAsync(_serviceUuid);
                if (service != null)
                {
                    var notifyCharacteristic = await service.GetCharacteristicAsync(_characteristicUuid);
                    if (notifyCharacteristic.Properties.HasFlag(CharacteristicPropertyType.Notify))
                    {
                        // 特性支持通知
                        // 订阅事件
                        notifyCharacteristic.ValueUpdated += NotifyCharacteristic_ValueUpdated;
                        // 启用通知
                        await notifyCharacteristic.StartUpdatesAsync();
                    }
                }
            }
            catch(Exception ex)
            {

            }
            return 0;
        }
        // 处理特性值更新事件
        private void NotifyCharacteristic_ValueUpdated(object sender, Plugin.BLE.Abstractions.EventArgs.CharacteristicUpdatedEventArgs e)
        {
            // 处理特性值更新
            NotifyQueue.Enqueue( e.Characteristic.Value);
            // ...
        }
        public async Task<byte[]?> ReadNotify(IDevice device)
        {
            if (device.IsConnectable == true)
            {
                if (NotifyQueue.Count > 0)
                {
                    return NotifyQueue.Dequeue();
                }
            }
            return null;
        }

        private async Task<int> StopNotify(IDevice device)
        {
            var service = await device.GetServiceAsync(_serviceUuid);
            if (service != null)
            {
                // 获取服务和特性
                var notifyCharacteristic = await service.GetCharacteristicAsync(_characteristicUuid);
                if (notifyCharacteristic.Properties.HasFlag(CharacteristicPropertyType.Notify))
                {
                    // 特性支持通知
                    // 订阅事件
                    notifyCharacteristic.ValueUpdated -= NotifyCharacteristic_ValueUpdated;
                    // 启用通知
                    await notifyCharacteristic.StopUpdatesAsync();
                }

            }
            return 0;
        }
        #endregion

 

4. 跳绳设备对接实践

        在实际对接跳绳设备时,我们需要根据设备的技术文档来确定服务和特征的UUID。一旦确定,就可以按照上述步骤进行连接和数据交换。例如,读取跳绳次数的特征可能有一个特定的UUID,我们可以通过这个UUID来读取或写入数据。

5. 注意事项

  • 确保在开发过程中,手机的蓝牙功能处于开启状态。
  • 在配对设备时,确保手机与跳绳设备的距离足够近,以保证信号的稳定性。
  • 在读取和写入数据时,要注意数据格式和编码方式,确保数据的正确解析。

通过上述步骤和注意事项,可以在MAUI中顺利实现与BLE设备的对接,记录跳绳的次数与时间。希望这些经验能够帮助你在开发过程中少走弯路,快速实现功能

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2252599.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

centos7调用so库无响应

这里使用centos7部署一个springboot项目调用so库函数时&#xff0c;无任何响应与输出、也无任何报错信息。以下是我使用的服务器信息&#xff1a; 解决方法&#xff1a; 这里我先直接说下解决方法。有效的解决方法是将你的 so 库文件上传至服务器的 lib 目录中&#xff0c;并对…

使用 PDF API 合并 PDF 文件

内容来源&#xff1a; 如何在 Mac 上合并 PDF 文件 1. 注册与认证 您可以注册一个免费的 ComPDFKit API 帐户&#xff0c;该帐户允许您在 30 天内免费无限制地处理 1,000 多个文档。 ComPDFKit API 使用 JSON Web Tokens 方法进行安全身份验证。从控制面板获取您的公钥和密钥&…

多线程相关案例

目录 1. 单例模式 1) 饿汉模式 2) 懒汉模式 2. 阻塞队列 1) 阻塞队列的特性 2) 模拟实现阻塞队列 3. 定时器 4. 线程池 1) ThreadPoolExecutor 类 2) 模拟实现线程池 1. 单例模式 单例模式是最经典的设计模式之一。 单例模式&#xff0c;顾名思义&#xff0c;就是这…

【PyQt入门】麦当劳会员登录页面实战

PyQt思维导图&#xff1a; 效果图如下&#xff1a; 设计页面包含&#xff1a;图标&#xff08;含动图gif&#xff09;&#xff0c;窗口logo&#xff0c;title&#xff0c;文本框&#xff0c;按钮 素材图如下&#xff1a; 完整代码以及标注如下&#xff1a; # 导入必要的PyQt6…

中断,定时器相关内容

中断&#xff0c;定时器相关内容 单片机中断什么是单片机的中断中断嵌套中断的优点中断结构中断相关寄存器中断优先级IP中断号中断响应条件中断使用实例在这里插入代码片 定时器CPU 时序的有关知识定时器原理定时计数结构定时/计数器的寄存器定时器配置功能实现 单片机中断 高位…

五层网络协议(封装和分用)

目录 七层网络协议五层网络协议封装1.应用层2.传输层3.网络层4.数据链路层5.物理层 分用1. 物理层2.数据链路层3.网络层 IP 协议4.传输层 UDP 协议5.应用层 七层网络协议 网络通信过程中&#xff0c;需要涉及到的细节&#xff0c;其实是非常非常多的&#xff0c;如果要有一个协…

在鲲鹏麒麟服务器上部署MySQL主从集群

因项目需求需要部署主从MySQL集群&#xff0c;继续采用上次的部署的MySQL镜像arm64v8/mysql:latest&#xff0c;版本信息为v8.1.0。计划部署服务器192.168.31.100和192.168.31.101 部署MySQL主节点 在192.168.31.100上先创建好/data/docker/mysql/data和/data/docker/mysql/l…

一款支持80+语言,包括:拉丁文、中文、阿拉伯文、梵文等开源OCR库

大家好&#xff0c;今天给大家分享一个基于PyTorch的OCR库EasyOCR&#xff0c;它允许开发者通过简单的API调用来读取图片中的文本&#xff0c;无需复杂的模型训练过程。 项目介绍 EasyOCR 是一个基于Python的开源项目&#xff0c;它提供了一个简单易用的光学字符识别&#xff…

C++学习日记---第16天

笔记复习 1.C对象模型 在C中&#xff0c;类内的成员变量和成员函数分开存储 我们知道&#xff0c;C中的成员变量和成员函数均可分为两种&#xff0c;一种是普通的&#xff0c;一种是静态的&#xff0c;对于静态成员变量和静态成员函数&#xff0c;我们知道他们不属于类的对象…

如何搭建JMeter分布式集群环境来进行性能测试

在性能测试中&#xff0c;当面对海量用户请求的压力测试时&#xff0c;单机模式的JMeter往往力不从心。如何通过分布式集群环境&#xff0c;充分发挥JMeter的性能测试能力&#xff1f;这正是许多测试工程师在面临高并发、海量数据时最关注的问题。那么&#xff0c;如何轻松搭建…

人工智能-卷积神经网络(学习向)

一.概述&#xff1b; 卷积神经网络&#xff08;Convolutional Neural Network, CNN&#xff09;是一种专门用于处理具有类似网格结构的数据&#xff08;如图像&#xff09;的深度学习模型。 主要用于处理机器视觉任务。 主要功能&#xff1b; 1.图像分类 2.目标检测 3.图像分割…

一些基于宏基因组的巨型病毒研究

Introduction 上次已经介绍了巨型病毒的一些基本内容&#xff0c;也讲到了不依赖培养的方法是从环境样本中发现巨型病毒基因组成的不可或缺的工具。可以通过基因组解析宏基因组学来从环境序列数据中获取 NCLDV 基因组并进行深入研究如功能基因&#xff0c;宿主&#xff0c;进化…

【Django-xadmin】

时间长不用,会忘的系列 1、Django-xadmin后台字段显示处理 主要是修改每个模块下adminx.py文件 代码解释&#xff1a;第1行控制表单字段显示第2行控制列表字段显示第3行控制搜索条件第4行控制过滤条件第5行支持单个或多个字段信息修改第6行列表分页&#xff0c;每页显示多少行…

深入浅出体验AI生图产品Dall-E

DALL-E是由OpenAI开发的一种革命性的AI图像生成工具&#xff0c;能够根据文本描述生成图像。它的名字灵感来源于著名画家萨尔瓦多达利&#xff08;Salvador Dal&#xff09;和皮克斯动画电影中的角色瓦力&#xff08;WALL-E&#xff09;&#xff0c;这暗示了其在艺术创造力与技…

域名解析系统 DNS

1.域名系统概述 用户与互联网上某台主机通信时&#xff0c;必须要知道对方的IP地址。然而用户很难记住长达32 位的二进制主机地址。即使是点分十进制地址也并不太容易记忆。但在应用层为了便于用户记忆各种网络应用&#xff0c;连接在互联网上的主机不仅有P地址&#xff0c;而…

学习ASP.NET Core的身份认证(基于Session的身份认证3)

开源博客项目Blog中提供了另一种访问控制方式&#xff0c;其基于自定义类及函数的特性类控制访问权限。本文学习并测试开源博客项目Blog的访问控制方式&#xff0c;测试程序中直接复用开源博客项目Blog中的相关类及接口定义&#xff0c;并在其上调整判断逻辑。   首先是接口A…

十六(AJAX3)、XMLHttpRequest、Promise、简易axios封装、案例天气预报、lodash-debounce防抖

1. XMLHttpRequest 1.1 XMLHttpRequest-基本使用 /* 定义&#xff1a;XMLHttpRequest&#xff08;XHR&#xff09;对象用于与服务器交互。通过 XMLHttpRequest 可以在不刷新页面的情况下请求特定 URL&#xff0c;获取数据。这允许网页在不影响用户操作的情况下&#xff0c;更…

【QT】音乐播放器demo

1、使用设计师模式绘制ui界面 添加QPushButton并设置大小&#xff0c;ctrl鼠标拖动复制相同的组件。 添加icon //ps:icon下载网站 设置按钮无边框并设置鼠标悬停颜色&#xff1a; 修改QWidget样式表&#xff0c;添加&#xff1a; *{ border:none; } QPushBu…

「Mac畅玩鸿蒙与硬件34」UI互动应用篇11 - 颜色选择器

本篇将带你实现一个颜色选择器应用。用户可以从预设颜色中选择&#xff0c;或者通过输入颜色代码自定义颜色来动态更改界面背景。该应用展示了如何结合用户输入、状态管理和界面动态更新的功能。 关键词 UI互动应用颜色选择器状态管理用户输入界面动态更新 一、功能说明 颜色…

T620存储安全方案SoC芯片技术手册

系统资源 集成32位国产CPU CK803S&#xff1b;最高工作频率260Mhz CK803S内置16KB I/D Cache&#xff0c;内置32KB DTCM 32KB ROM&#xff1b;256KB SRAM&#xff1b;8KB SRAM&#xff08;系统专用&#xff09; 512KB/1MB 片内Flash 安全算法 支持SM4数据加密&#xff0c;加密性…