Skip to content

iOS

🚀 准备工作

硬件连接指引

有关头环设备的开关机操作、状态指示灯含义及各端 SDK 的设备配对说明,请在开发前优先查阅:设备使用说明与常见问题 (FAQ)

编译环境要求

  • 开发工具:Xcode 14.0 及以上版本。
  • 系统层门槛:应用部署目标(Deployment Target)必须设置为 iOS 12.0 或更高
  • 开发语言:Swift 5.0 及以上版本。

安装和集成

导入 SDK (CocoaPods)

推荐使用 CocoaPods 集成 OxyZen iOS SDK。在您的 Podfile 中添加以下依赖:

ruby
source 'https://github.com/BrainCoTech/cocoapods-specs.git'
source 'https://cdn.cocoapods.org/'

platform :ios, '12.0'
use_frameworks!

target 'YourAppTarget' do
  pod 'ZenLiteSDK', '=1.5.2'
end

执行安装配置:

bash
pod install

所需权限

在项目的 Info.plist 中,必须声明蓝牙权限及后台运行策略(若需要):

xml
<!-- 蓝牙权限说明 -->
<key>NSBluetoothAlwaysUsageDescription</key>
<string>此应用需要蓝牙权限来连接和管理 OxyZen 设备</string>

<!-- 后台运行配置(可选) -->
<key>UIBackgroundModes</key>
<array>
    <string>bluetooth-central</string>
</array>

API 核心组件参考

💡 SDK 架构说明

OxyZen iOS SDK 在架构上划分为以下两个模块(代码中的 ZenLite 前缀为产品曾用名,类名保持向后兼容):

  1. ZenLiteSDK(通过 CocoaPods 引入):闭源的核心引擎,负责蓝牙原始数据解析、信号滤波及各类算法指标(如专注度、冥想指数、睡眠分期等)的计算与输出。
  2. BLE 通信层:负责设备扫描、连接管理与指令交互。该模块的完整源码已在 示例工程 中公开,开发者可根据自身需求对扫描策略、连接保活等逻辑进行自定义修改,也可以将其整体替换为自己的 BLE 通信实现。

BLEDeviceManager (Class)

负责 BLE 设备的全局扫描与管理:

swift
// 开启设备扫描
BLEDeviceManager.shared.startScan()

// 设置扫描回调代理
BLEDeviceManager.scannerDelegate = self

// 停止设备扫描
BLEDeviceManager.shared.stopScan()

ZenLiteDevice (Class)

设备控制和数据通信的核心设备对象:

swift
@objc public class ZenLiteDevice: NSObject {
    // 基础属性
    public weak var delegate: ZenLiteDelegate?
    public private(set) var id: String
    public private(set) var name: String
    public private(set) var inPairngMode: Bool
    public private(set) var batteryLevel: Int
    public private(set) var rssi: Int
    public private(set) var deviceInfo: DeviceInfo
    public private(set) var connectivity: Connectivity
    public var contactState: DeviceContactState
    
    // 连接
    func connect()
    func disconnect()
    
    // 配对
    // 在配对模式下(inPairngMode)或者重连时需要调用
    func pair(onResponse: @escaping ZenLiteRespCallback)
    
    // 综合数据流控制
    func startDataStream()
    func stopDataStream()
    
    // EEG 数据流控制
    func startEEG()
    func stopEEG()
    func configAfe(_ sampleRate: AfeDataSampleRate)
    
    // IMU 数据流控制
    func startImu()
    func stopImu()
    func configImu(_ sampleRate: ImuDataSampleRate, _ mode: ImuMode)
    
    // PPG 数据流控制
    func startPpg()
    func stopPpg()
    func configPpg(_ sampleRate: PpgDataSampleRate, _ mode: PpgMode)
    
    // 设备设置
    func setDeviceName(_ name: String, onResponse: @escaping ZenLiteRespCallback)
}

ZenLiteDelegate (Protocol)

用于接收设备事件与生理指标的数据回调监听器:

swift
@objc public protocol ZenLiteDelegate {
    // 错误处理
    @objc optional func onError(_ error: ZenLiteError)
    
    // 设备与系统信息
    @objc optional func onDeviceInfoReady(_ deviceInfo: DeviceInfo)
    @objc optional func onSystemInfo(_ systemInfo: SystemInfo)
    
    // 连接与硬件状态
    @objc optional func onConnectivityChange(_ connectivity: Connectivity)
    @objc optional func onBatteryLevelChange(_ batteryLevel: Int)
    @objc optional func onContactStateChange(_ contactState: DeviceContactState)
    @objc optional func onOrientationChange(_ orientation: Orientation)
    
    // 传感器数据 - 脑电
    @objc optional func onEEGData(_ eeg: OxyZenEEG)
    @objc optional func onRawEEGData(_ eeg: OxyZenEEG)
    @objc optional func onBrainWave(_ wave: BrainWave)
    
    // 传感器数据 - 运动学与光学
    @objc optional func onIMUData(_ imu: OxyZenIMU)
    @objc optional func onPPGData(_ ppg: PPG)
    @objc optional func onSignalQualityWarning(quality: Int)
    
    // 算法指标
    @objc optional func onAttention(attention: Float)
    @objc optional func onMeditation(meditation: Float, calmness: Float, awareness: Float)
    @objc optional func onSleep(stage: SleepStageType, conf: Float, drowsiness: Int)
}

数据模型

DeviceInfo 设备信息

swift
struct DeviceInfo {
    var manufacturerName: String   // 制造商名称
    var modelNumber: String        // 型号
    var serialNumber: String       // 序列号
    var hardwareRevision: String   // 硬件版本
    var firmwareRevision: String   // 固件版本
}

OxyZenEEG 脑电波数据

swift
@objc public class OxyZenEEG: NSObject {
    @objc public let sequenceNumber: Int    // 序列号
    @objc public let sampleRate: Float      // 采样率
    @objc public let eegData: [Float]       // EEG 数据,每组 30 个
}

BrainWave 脑波频段指标

swift
@objc public class BrainWave: NSObject {
    @objc public let delta: Double          // Delta 波 (0.5-4 Hz)
    @objc public let theta: Double          // Theta 波 (4-8 Hz)
    @objc public let alpha: Double          // Alpha 波 (8-13 Hz)
    @objc public let lowBeta: Double        // Low Beta 波 (13-17 Hz)
    @objc public let highBeta: Double       // High Beta 波 (17-30 Hz)
    @objc public let gamma: Double          // Gamma 波 (30-50 Hz)
}

OxyZenIMU 惯性测量单元数据

swift
@objc public class OxyZenIMU: NSObject {
    @objc public var accData: ACC?          // 加速度计数据
    @objc public var gyroData: Gyro?        // 陀螺仪数据
    @objc public var eulerAngleData: EulerAngle? // 欧拉角数据
    @objc public var sampleRate: Float      // 采样率
    @objc public var head: Int              // 头部方向
    @objc public var body: Int              // 身体方向
}

// 加速度计数据
@objc public class ACC: NSObject {
    @objc public let sequenceNumber: Int
    @objc public var x: [Float]             // X 轴加速度
    @objc public var y: [Float]             // Y 轴加速度
    @objc public var z: [Float]             // Z 轴加速度
}

// 陀螺仪数据
@objc public class Gyro: NSObject {
    @objc public let sequenceNumber: Int
    @objc public var x: [Float]             // X 轴角速度
    @objc public var y: [Float]             // Y 轴角速度
    @objc public var z: [Float]             // Z 轴角速度
}

// 欧拉角数据
@objc public class EulerAngle: NSObject {
    @objc public var yaw: [Float]           // 偏航角 (-180 ~ 180)
    @objc public var pitch: [Float]         // 俯仰角 (-180 ~ 180)
    @objc public var roll: [Float]          // 翻滚角 (-180 ~ 180)
}

PPG 光电容积脉搏波数据

swift
@objc public class PPG: NSObject {
    @objc public var sequenceNumber: Int    // 序列号
    @objc public var reportRate: Float      // 上报频率
    @objc public var rawData: [PPGRaw]?     // 原始数据
    @objc public var algoData: [PPGAlgo]?   // 算法指标
    @objc public var respiratoryRate: Float // 呼吸频率
    @objc public var respiratoryCurve: [Float]? // 呼吸曲线
    @objc public var respiratoryState: Int  // 呼吸状态
}

// PPG 算法指标
@objc public class PPGAlgo: NSObject {
    @objc public let hr: Float              // 心率
    @objc public let hr_conf: Int           // 心率置信度
    @objc public let rr: Float              // 呼吸率
    @objc public let rr_conf: Int           // 呼吸率置信度
    @objc public let activity: Int          // 活动状态
    @objc public let spo2: Float            // 血氧饱和度
    @objc public let spo2_r: Float          // 血氧 R 值
    @objc public let spo2_conf: Int         // 血氧置信度
    @objc public let spo2_progress: Int     // 血氧测量进度
    @objc public let hrv: Float             // 心率变异性
    @objc public let hrv_stress: Float      // HRV 压力指数
    @objc public let stress: Float          // 压力指数
    @objc public let spo2_state: Int        // 血氧状态
    @objc public let contact_state: Int     // 接触状态
}

// PPG 原始数据
@objc public class PPGRaw: NSObject {
    @objc public let green1: Int            // 绿光 1 计数
    @objc public let green2: Int            // 绿光 2 计数
    @objc public let ir: Int                // 红外光计数
    @objc public let red: Int               // 红光计数
}

枚举常量类

连接状态

swift
@objc public enum Connectivity: Int, CaseIterable {
    case connecting = 0     // 连接中
    case connected = 1      // 已连接
    case disconnecting = 2  // 断开中
    case disconnected = 3   // 已断开
}

接触状态

swift
@objc public enum DeviceContactState: Int, CaseIterable {
    case unknown = 0        // 未知
    case off = 1            // 未正确佩戴
    case eeg = 2            // EEG 接触良好
    case all = 3            // EEG & PPG 接触良好
}

方向

swift
@objc public enum Orientation: Int, CaseIterable {
    case unknown = 0        // 未知
    case upward = 1         // 设备佩戴正确
    case downward = 2       // 设备戴反了
}

睡眠分期

swift
@objc public enum SleepStageType: Int, CaseIterable {
    case unknown = -1       // 未知
    case awake = 0          // 清醒
    case rem = 1            // 快速眼动
    case light = 2          // 浅睡眠
    case deep = 3           // 深睡眠
}

EEG 采样率

swift
@objc public enum AfeDataSampleRate: Int, CaseIterable {
    case none = 0           // 无
    case off = 1            // 关闭
    case sr128 = 2          // 128 Hz
    case sr256 = 3          // 256 Hz
}

IMU 采样率

swift
@objc public enum ImuDataSampleRate: Int, CaseIterable {
    case none = 0           // 无
    case off = 1            // 关闭
    case sr25 = 2           // 25 Hz
    case sr50 = 3           // 50 Hz
    case sr100 = 4          // 100 Hz
    case sr200 = 5          // 200 Hz
    case sr400 = 6          // 400 Hz
    case sr800 = 7          // 800 Hz
}

IMU 模式

swift
@objc public enum ImuMode: Int, CaseIterable {
    case none = 0           // 无
    case acc = 1            // 仅加速度计
    case gyro = 2           // 仅陀螺仪
    case acc_gyro = 3       // 加速度计 + 陀螺仪
    case euler = 4          // 欧拉角
}

PPG 采样率

swift
@objc public enum PpgDataSampleRate: Int, CaseIterable {
    case none = 0           // 无
    case off = 1            // 关闭
    case sr1 = 2            // 1 Hz
    case sr5 = 3            // 5 Hz
    case sr25 = 4           // 25 Hz
    case sr50 = 5           // 50 Hz(HRV 等需要更高频率)
    case sr100 = 6          // 100 Hz
}

PPG 模式

swift
@objc public enum PpgMode: Int, CaseIterable {
    case none = 0           // 无
    case raw_data = 1       // 原始数据
    case algo = 2           // 算法指标
    case spo2 = 3           // 仅血氧
    case hr = 4             // 仅心率
    case hrv = 5            // 心率变异性
}

故障排查与注意事项

常见连接与配对问题

若出现“无法扫描到设备”、“连接失败”或“配对失败”等通用蓝牙排障场景,请优先参考全局指南中的时序说明和问题排查指引:

👉 设备使用说明与常见问题 (FAQ)

CocoaPods 依赖获取失败

若在执行 pod install 时阻塞或报错:

  1. 检查网络环境及代理配置,并更新 CocoaPods 至最新版本(sudo gem install cocoapods)。
  2. 请尝试清理本地缓存并重新执行:
    bash
    pod cache clean --all
    pod deintegrate
    pod install

iOS 平台开发注意事项

  • UI 线程更新:SDK 中所有数据回调(如 onEEGData 等)均由底层蓝牙代理分发。若需更新界面元素,开发者必须确保将其调度至主线程(DispatchQueue.main.async 或 Swift 6 MainActor),以防触发不可预知的线程安全问题及崩溃。
  • 内存安全与避免循环引用:蓝牙设备的监听具有异步时序特点,当使用 Closures 控制请求回调或声明 Delegate 时,请注意使用 [weak self] 以避免造成视图控制器的循环引用(Retain Cycle)。
  • 资源清理策略:由于硬件数据吞吐量较大(特别是 256Hz EEG),建议在视图消失(viewWillDisappear)或 App 切入后台(若未申请后台运行权限)时,主动请求关闭对应的数据通道(如调用 device.stopDataStream()),节省设备耗电;在真正不需要连接时执行 device.disconnect() 释放句柄。

📖 完整集成示例

以上 API 参考涵盖了 SDK 核心组件的功能定义。为了协助开发者更直观地将各个模块组合应用至实际的 iOS 工程体系中,我们提供了一套开源参考实现。

该工程利用原生的 UIKit 与 Storyboard 构建,重点演示了:

  • 结合系统 CoreBluetooth 和 SDK 内置能力的扫描和鉴权全流程。
  • iOS 权限安全体系下的时序调用策略。
  • 采用 DGCharts 之类的图形库在主线程展示实时生理参数的时延优化。

查阅具体代码与完整应用层实现:

👉 OxyZen iOS 示例工程