MacOS上 Bluetooth Low Energy/BLE 应用的Swift语言开发笔记

2024-03-16 02:10

本文主要是介绍MacOS上 Bluetooth Low Energy/BLE 应用的Swift语言开发笔记,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1. 背景

最近由于各种原因打算做一个小gadget,这个小玩具需要用到BLE和PC/MAC通讯。作为一枚程序开发完完全全的新手,中途自然碰壁无数。而且说真的,本来在电脑上面用BLE就是比较罕见的奇葩要求,又找不到随手可用的教(copy)例(paste),总结一些经验教训(code),以供后来人参考。

2. BLE和各种蓝牙区别

首先要注意一下,BLE和以前普通标准的蓝牙不同!两者不通用!

现在常见的蓝牙技术大概有三种,加上新推出的5代:
1. Bluetooth Basic Rate_Enhanced Data Rate 也就是 BR_EDR
2. Bluetooth Low Energy 也就是 BLE
3. Bluetooth Smart 和 Smart Ready 大致是一种企图融合上面两种制式的过渡技术
Fig 1
4. Bluetooth 5.0 是最新的规格。买了chip有空再弄。

3. BLE蓝牙协议和实际利用

3.1 具体协议内容和连接方式

详情可以自行百度,或者看原文
协议和栈方面比较详细的介绍有:
蓝牙协议分析(1)_基本概念
蓝牙协议分析(2)_协议架构
蓝牙协议分析(3)_蓝牙低功耗(BLE)协议栈介绍
还有这个:BLE低功耗蓝牙介绍 - CSDN博客

当然了解具体协议和内容是非常非常重要的,不过对于日常小型应用来说,我们知道怎么发送怎么接受信息就可以了。
重点就是理解下图的Services和Characteristics
Fig 2

举个例子,BLE栈就像一个超市,里面有各个部署区域(Services),比方说:
* 蔬果区(HIDS/HID Service 人机交互服务)
* 熟食区(GLS/Glucose Service 血糖服务)
* 零食区(HRM/Heart Rate Monitor 心率监控服务 UUID=0x180D)

而每个部署区域都有货架,比如零食区(HRM心率)里面就有的:
* 巧克力架(Heart Rate Measurement, Value, UUID=0x2A37 心跳率)
* 饼干架(Heart Rate Sensor Location, Value, UUID=0x2A38 传感器位置)

例如,你想买牛奶巧克力,那就先要跑去零食区(UUID=0x180D),然后找到巧克力货架(UUID=0x2A37),et Voila!

这个Services和Characteristics都是用UUID来标记,都是约定俗成的,只能跑去看GATT里面的定义。也可以自行生成UUID:Online UUID Generator

-(当然有人会问那你想搞个没有定义或者不符合定义的数据的通讯怎么办呢?有个做法是挂羊头卖狗肉,当然Central和Peripheral两头设定都要对得上。)-

3.2 Central和Peripheral

一般来说Central是读取信息的一方,Peripheral是提供信息的一方现在的情况的话电脑是Central了。
Fig 3

3.3 比较有用的测试道具

Mac上面有两个道具比较有用:LightBlue和PacketLogger
Fig 4
Fig 5

iOS上面有:LightBlue,nRF Connect,nRF Toolbox

4. MacOS BLE App Swift编程的注意点

其实iOS的BLE程序的代码差不多改改UI组件就可以在MacOS上面使用,但是在Xcode里面编程调试MacOS Swift代码需要注意几个不同之处

4.1 XPC connection invalid

  1. 在MacOS的Xcode里面,打开沙盒(App Sandbox)里面的蓝牙
    路径:target -> Capabilities -> App Sandbox -> Hardware -> Bluetooth
    Fig 6

  2. info.plist 里面追加 Privacy - Bluetooth Peripheral Usage Description,Value随意
    Fig 7

好了,准备完成!现在可以开始写代码了。
使用的swift版本是4, MacOS是High Sierra 10.13.3


5.1 初始化

  • 导入库
import CoreBluetooth
  • 导入delegate
class ViewController: NSViewController, CBCentralManagerDelegate, CBPeripheralDelegate {}
  • 实例化
var MacCentralManager: CBCentralManager!
var TargetPeripheral: CBPeripheral!
var TargetCharacteristic: CBCharacteristic!
  • 初始化启动
MacCentralManager = CBCentralManager(delegate: self, queue: nil)
  • 定义UUID之类
let heartRateServiceCBUUID = CBUUID(string: "0x180D")
let UUID1 = CBUUID(string: "2A37")    //这个UUID是心率数据的特征
let UUID2 = CBUUID(string: "2A38")    //这个UUID是心率传感器位置的特征

5.2 检查蓝牙设备状态并开启搜索(必做)

每次启动了APP必须首先检查本机蓝牙设备的状态是否在正常运作,否则以后代码很可能报错
[CoreBluetooth] API MISUSE: <CBCentralManager: 0x17426af00> can only accept this command while in the powered on state

func centralManagerDidUpdateState(_ central: CBCentralManager) {
//检查蓝牙设备是否有在更新if central.state == CBManagerState.poweredOn { print("did update:\(central.state)")//蓝牙设备确实有反应了才开始搜索central.scanForPeripherals(withServices: nil,options: nil)print("Start Scanning")   //调试用} else {//蓝牙设备没有更新的话,报告原因print("BLE on this Mac is not ready")switch central.state {case .unknown:print("蓝牙的central.state is .unknown")case .resetting:print("蓝牙的central.state is .resetting")case .unsupported:print("蓝牙的central.state is .unsupported")case .unauthorized:print("蓝牙的central.state is .unauthorized")case .poweredOff:print("蓝牙的central.state is .poweredOff")case .poweredOn:print("蓝牙的central.state is .poweredOn") }}}

5.3 搜索目标BLE设备然后连接设备

func centralManager(_ central: CBCentralManager,didDiscover peripheral: CBPeripheral,advertisementData: [String : Any],rssi RSSI: NSNumber) {//陈列周围的BLE设备print("BLE Device identified: \(peripheral)")//寻找identifier代码含有P_UUID的周边设备if peripheral.identifier.uuidString.contains("P_UUID"){ TargetPeripheral = peripheralTargetPeripheral.delegate = self              //初始化peripheral的delegateMacCentralManager.stopScan() MacCentralManager.connect(TargetPeripheral)   //连接该peripheral}print("\(peripheral) is connected")               //调试用输出}

5.4 确认连接上了

func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {print("Connection Confirmed!")TargetPeripheral.discoverServices([heartRateServiceCBUUID])print("Target Service Confirmed!")}

不执行这一步的话下面代码会出错

5.5 搜索指定Services服务并陈列Characteristics特征

var TargetService: CBService!   // 实例化一般放在前面func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {guard let services = peripheral.services else { return }for service in services {print(service)//陈列所有的service和旗下的characteristicsperipheral.discoverCharacteristics(nil, for: service)   //寻找指定UUID的服务if service.uuid.uuidString.contains("UUID") { print("Identified TargetServices \(service.uuid.uuidString)")TargetService = service as CBService  }}}

5.6 找出指定的characteristics特征并读取

func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {guard let characteristics = TargetService.characteristics else { return }print("Existing Characteristics identified")    //调试用输出for characteristic in characteristics {print(characteristic)//陈列所有read方式的特征if characteristic.properties.contains(.read) {print("\(characteristic.uuid): properties contains .read")  //找到read方式的characteristics并读取peripheral.readValue(for: characteristic)}//陈列所有notify方式的特征if characteristic.properties.contains(.notify) {print("\(characteristic.uuid): properties contains .notify")  //找到notify方式的characteristics并读取peripheral.setNotifyValue(true, for: characteristic)}//也可以找指定UUID的characteristic特征if characteristic.properties.contains("UUID") {print("\(characteristic.uuid): properties contains UUID")  //找到指定UUID的特征然后下面采取read或者notifyperipheral.setNotifyValue(true, for: characteristic)peripheral.readValue(for: characteristic)}}}

5.7 监控Characteristics新数据并提取

func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic:  CBCharacteristic, error: Error?) {switch characteristic.uuid {case UUID1:guard let characteristicData1 = characteristic.value//这里的characteristicData1就是读取成功的数据啦!//自由添加代码自己爱怎么操作就怎么操作case UUID2:guard let characteristicData2 = characteristic.value//这里的characteristicData2就是读取成功的数据啦!//自由添加代码自己爱怎么操作就怎么操作default:print("Unhandled Characteristic UUID: \(characteristic.uuid)")}}

5.8 写数据

TargetPeripheral.writeValue(dataToTrans, for:   WriteCharactistic, type: CBCharacteristicWriteType.withResponse)

type还有withoutResponse

6. 总结

以上介绍了MacOS连接BLE设备最简单的基本操作流程,利用以上代码基本可以进行初级的读写数据。
如有错误还请指出。

MacOS的代码例子有空再写~
iOS上面的比较好的GitHub例子:https://github.com/DennisMao/eswiftBLE a.k.a 【Mac/ios】Swift3.0 BLE开发 - RazilFelix的博客 - CSDN博客

其他诸多参考

[1]iOS×BLE Core Bluetoothプログラミング

[2]iOS SwiftでBLEのサンプルを動かしてみる

[3]macOS x mbed OS でBLE通信する方法

[4]Core Bluetooth Tutorial for iOS: Heart Rate Monitor

[5]swift使ってEddystone-URLを受信するiOSアプリを作った - BlankTar

[6]iOS蓝牙之Introduction to Core Bluetooth: Building a Heart Rate Monitor(翻译) - CSDN博客

[7]BLEのペリフェラルを今更実装してみた(iOS編)

[8]Getting started with Bluetooth Low Energy on iOS

[9]Core Bluetooth Programming Guide Apple

վ HᴗP ի

这篇关于MacOS上 Bluetooth Low Energy/BLE 应用的Swift语言开发笔记的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/813966

相关文章

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

zoj3820(树的直径的应用)

题意:在一颗树上找两个点,使得所有点到选择与其更近的一个点的距离的最大值最小。 思路:如果是选择一个点的话,那么点就是直径的中点。现在考虑两个点的情况,先求树的直径,再把直径最中间的边去掉,再求剩下的两个子树中直径的中点。 代码如下: #include <stdio.h>#include <string.h>#include <algorithm>#include <map>#

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来