IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> Unity3D蓝牙连接Mi Band 5手环获取实时心率 -> 正文阅读

[游戏开发]Unity3D蓝牙连接Mi Band 5手环获取实时心率

Unity3D蓝牙连接Mi Band 5手环获取实时心率
直接上代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class BLEMiBand : MonoBehaviour
{
  public class UUIDS
  {
    public static readonly string miband1 = "0000fee0-0000-1000-8000-00805f9b34fb";
    public static readonly string miband2 = "0000fee1-0000-1000-8000-00805f9b34fb";
    public static readonly string alert = "00001802-0000-1000-8000-00805f9b34fb";
    public static readonly string devinfo = "0000180a-0000-1000-8000-00805f9b34fb";
    public static readonly string heartrate = "0000180d-0000-1000-8000-00805f9b34fb";
    public static readonly string notifications = "00001811-0000-1000-8000-00805f9b34fb";
  }
  public class CHAR_UUIDS
  {
    public static readonly string devicename = "00002a00-0000-1000-8000-00805f9b34fb";

    public static readonly string hz = "00000002-0000-3512-2118-0009af100700";
    public static readonly string sensor = "00000001-0000-3512-2118-0009af100700";
    public static readonly string auth = "00000009-0000-3512-2118-0009af100700";

    public static readonly string alert = "00002a06-0000-1000-8000-00805f9b34fb";
    public static readonly string current_time = "00002a2b-0000-1000-8000-00805f9b34fb";
    // unknown=         "00002a23-0000-1000-8000-00805f9b34fb";
    public static readonly string serial = "00002a25-0000-1000-8000-00805f9b34fb";
    public static readonly string hrdw_revision = "00002a27-0000-1000-8000-00805f9b34fb";
    public static readonly string revision = "00002a28-0000-1000-8000-00805f9b34fb";
    public static readonly string heartrate_measure = "00002a37-0000-1000-8000-00805f9b34fb";
    public static readonly string heartrate_control = "00002a39-0000-1000-8000-00805f9b34fb";
    public static readonly string notifications = "00002a46-0000-1000-8000-00805f9b34fb";
    // unknown=         "00002a50-0000-1000-8000-00805f9b34fb";
    public static readonly string age = "00002a80-0000-1000-8000-00805f9b34fb";
    public static readonly string le_params = "0000ff09-0000-1000-8000-00805f9b34fb";

    public static readonly string configuration = "00000003-0000-3512-2118-0009af100700";
    public static readonly string fetch = "00000004-0000-3512-2118-0009af100700";
    public static readonly string activity_data = "00000005-0000-3512-2118-0009af100700";
    public static readonly string battery = "00000006-0000-3512-2118-0009af100700";
    public static readonly string steps = "00000007-0000-3512-2118-0009af100700";
    public static readonly string user_settings = "00000008-0000-3512-2118-0009af100700";
    public static readonly string music_notification = "00000010-0000-3512-2118-0009af100700";
    public static readonly string deviceevent = "00000010-0000-3512-2118-0009af100700";
    public static readonly string chunked_transfer = "00000020-0000-3512-2118-0009af100700";
  }
  public class NOTIFICATION_TYPES
  {
    public static readonly byte[] auth = { 0x02, 0x00 };
    public static readonly byte[] msg = { 0x01, 0x01 };
    public static readonly byte[] call = { 0x03, 0x01 };
    public static readonly byte[] missed = { 0x04, 0x01 };
    public static readonly byte[] sms = { 0x05, 0x01 };
  }
  public class AUTH_TYPES
  {
    public static readonly byte[] received = { 0x10, 0x01, 0x01 };
    public static readonly byte[] requestcode = { 0x10, 0x02, 0x01 };
    public static readonly byte[] authed = { 0x10, 0x03, 0x01 };
    public static readonly byte[] authfail = { 0x10, 0x03, 0x08 };
  }
  public class HEARTRATECONTROL_TYPES
  {
    public static readonly byte[] stop_heartrate_manual = { 0x15, 0x01, 0x00 };
    public static readonly byte[] stop_heartrate_auto = { 0x15, 0x02, 0x00 };
    public static readonly byte[] start_heartrate_manual = { 0x15, 0x01, 0x01 };
    public static readonly byte[] start_heartrate_auto = { 0x15, 0x02, 0x01 };
    public static readonly byte[] ping = { 0x16 };
  }

  public string DeviceName = "Mi Smart Band 5";
  public string AuthKey = "50a2c4668b3e565c0f495885251c4346";

  public Text txtMiBandLabel;
  public Text txtMiBandStatus;
  public Text txtBluetoothStatus;
  public GameObject PanelMiddle;
  public Text txtHeartRate;

  enum States
  {
    None,
    Scan,
    Connect,
    Auth,
    Subscribe,
    Unsubscribe,
    Disconnect,
    Communication,
  }

  [SerializeField]
  private bool bConnected = false;
  [SerializeField]
  private bool bAuthed = false;
  [SerializeField]
  private bool bSubscribed = false;
  [SerializeField]
  private string device;

  [SerializeField]
  bool foundChartAuth = false;
  [SerializeField]
  bool foundChartHeartrateControl = false;
  [SerializeField]
  bool foundChartHeartrateMeasurement = false;
  [SerializeField]
  bool foundChartSensor = false;
  void Reset()
  {
    bConnected = false;
    bAuthed = false;
    device = null;
    foundChartAuth = false;
    foundChartHeartrateControl = false;
    foundChartHeartrateMeasurement = false;
    foundChartSensor = false;
    PanelMiddle.SetActive(false);
  }

  void InitBLE()
  {
    txtMiBandStatus.text = "";
    txtBluetoothStatus.text = "Initializing...";

    Reset();
    BluetoothLEHardwareInterface.Initialize(true, false, () => {

      StartCoroutine(delayScan(0.1f));
      txtBluetoothStatus.text = "Initialized";

    }, (error) => {
      BluetoothLEHardwareInterface.Log("Error: " + error);
    });
  }

  // Use this for initialization
  void Start()
  {
    InitBLE();
  }
  // Update is called once per frame
  void Update()
  {
  }
  IEnumerator delayScan(float delay)
  {
    yield return new WaitForSeconds(delay);
    txtBluetoothStatus.text = "Scanning devices[" + DeviceName + "]";

    BluetoothLEHardwareInterface.ScanForPeripheralsWithServices(null, (address, name) =>
    {
      // we only want to look at devices that have the name we are looking for
      // this is the best way to filter out devices
      if (name.Contains(DeviceName))
      {
        DeviceName = name;

        // it is always a good idea to stop scanning while you connect to a device
        // and get things set up
        BluetoothLEHardwareInterface.StopScan();
        txtBluetoothStatus.text = "";

        // add it to the list and set to connect to it
        device = address;

        txtMiBandLabel.text = DeviceName + "[" + device + "]";
        txtMiBandStatus.text = "Found " + DeviceName;

        StartCoroutine(delayConnect(0.5f));
      }
    }, null, false, false);
  }
  IEnumerator delayConnect(float delay)
  {
    yield return new WaitForSeconds(delay);

    // set these flags
    txtMiBandStatus.text = "Connecting to " + DeviceName;

    // note that the first parameter is the address, not the name. I have not fixed this because
    // of backwards compatiblity.
    // also note that I am note using the first 2 callbacks. If you are not looking for specific characteristics you can use one of
    // the first 2, but keep in mind that the device will enumerate everything and so you will want to have a timeout
    // large enough that it will be finished enumerating before you try to subscribe or do any other operations.
    BluetoothLEHardwareInterface.ConnectToPeripheral(device, null, null, (address, serviceUUID, characteristicUUID) =>
    {
      if (IsEqual(serviceUUID, UUIDS.miband2) && IsEqual(characteristicUUID, CHAR_UUIDS.auth))
      {
        foundChartAuth = true;
      }
      else if (IsEqual(serviceUUID, UUIDS.heartrate) && IsEqual(characteristicUUID, CHAR_UUIDS.heartrate_control))
      {
        foundChartHeartrateControl = true;
      }
      else if (IsEqual(serviceUUID, UUIDS.heartrate) && IsEqual(characteristicUUID, CHAR_UUIDS.heartrate_measure))
      {
        foundChartHeartrateMeasurement = true;
      }
      else if (IsEqual(serviceUUID, UUIDS.miband1) && IsEqual(characteristicUUID, CHAR_UUIDS.sensor))
      {
        foundChartSensor = true;
      }
      // all need characteristics found
      if (foundChartAuth && foundChartHeartrateControl && foundChartHeartrateMeasurement && foundChartSensor)
      {
        bConnected = true;
        txtMiBandStatus.text = "Connected to " + DeviceName;
        // if we have found the characteristic that we are waiting for
        // authenticate. make sure there is enough timeout that if the
        // device is still enumerating other characteristics it finishes
        // before we try to subscribe
        StartCoroutine(delayAuthenticate(12f));
      }
    }, (disconnectedAddress) =>
    {
      BluetoothLEHardwareInterface.Log("Device disconnected: " + disconnectedAddress);
      txtMiBandStatus.text = "Disconnected";
      bConnected = false;
      bAuthed = false;
      bSubscribed = false;
    });

  }
  IEnumerator delayAuthenticate(float delay)
  {
    PanelMiddle.SetActive(true);
    yield return new WaitForSeconds(delay);
    SubscribeAuth();
    yield return new WaitForSeconds(0.5f);
    SendAuth();
  }
  IEnumerator delaySubscribeHeartrate(float delay)
  {
    yield return new WaitForSeconds(delay);
    BluetoothLEHardwareInterface.WriteCharacteristic(device, UUIDS.heartrate, CHAR_UUIDS.heartrate_control,
      HEARTRATECONTROL_TYPES.stop_heartrate_auto, HEARTRATECONTROL_TYPES.stop_heartrate_auto.Length, false, (charUUID) =>
      {
      });
    yield return new WaitForSeconds(0.5f);
    BluetoothLEHardwareInterface.WriteCharacteristic(device, UUIDS.heartrate, CHAR_UUIDS.heartrate_control,
      HEARTRATECONTROL_TYPES.stop_heartrate_manual, HEARTRATECONTROL_TYPES.stop_heartrate_manual.Length, false, (charUUID) =>
      {
      });
    yield return new WaitForSeconds(0.5f);
    BluetoothLEHardwareInterface.SubscribeCharacteristicWithDeviceAddress(device, UUIDS.heartrate, CHAR_UUIDS.heartrate_measure, null, (address, characteristicUUID, bytes) =>
    {
      txtMiBandStatus.text = "Received Subscribe[" + bytes.Length.ToString() + "][" + Time.time.ToString("F2") + "]" + Utility.Bytes2Hex(bytes) + "";
      if (2 == bytes.Length)
      {
        byte[] heartrate = { bytes[1], bytes[0] };
        txtHeartRate.text = System.BitConverter.ToInt16(heartrate, 0).ToString("D3") + "BPM";
      }
      else if (1 == bytes.Length)
      {
        txtHeartRate.text = ((int)(bytes[0])).ToString("D3") + "BPM";
      }
    });
    yield return new WaitForSeconds(0.5f);
    txtMiBandStatus.text = "Starting manual heart rate detection...";
    BluetoothLEHardwareInterface.WriteCharacteristic(device, UUIDS.heartrate, CHAR_UUIDS.heartrate_control,
      HEARTRATECONTROL_TYPES.start_heartrate_manual, HEARTRATECONTROL_TYPES.start_heartrate_manual.Length, false, (charUUID) =>
      {
        txtMiBandStatus.text = "Startedmanual heart rate detection...";
      });
    while (bSubscribed)
    {
      yield return new WaitForSeconds(12f);
      BluetoothLEHardwareInterface.WriteCharacteristic(device, UUIDS.heartrate, CHAR_UUIDS.heartrate_control,
        HEARTRATECONTROL_TYPES.ping, HEARTRATECONTROL_TYPES.ping.Length, false, (charUUID) =>
        {
        });
    }
  }
  IEnumerator delayUnsubscribeHeartrate(float delay)
  {
    yield return new WaitForSeconds(delay);
    BluetoothLEHardwareInterface.UnSubscribeCharacteristic(device, UUIDS.heartrate, CHAR_UUIDS.heartrate_measure, null);
    bSubscribed = false;
    StartCoroutine(delayDisconnect(4f));
  }
  IEnumerator delayDisconnect(float delay)
  {
    yield return new WaitForSeconds(delay);
    if (bConnected)
    {
      BluetoothLEHardwareInterface.DisconnectPeripheral(device, (address) =>
      {
        BluetoothLEHardwareInterface.DeInitialize(() =>
        {
          bConnected = false;
        });
      });
    }
    else
    {
      BluetoothLEHardwareInterface.DeInitialize(() =>
      {
      });
    }
  }

  public void SubscribeAuth()
  {
    txtMiBandStatus.text = "Subscribe Characteristic[CHAR_UUIDS.auth]";
    BluetoothLEHardwareInterface.SubscribeCharacteristic(device, UUIDS.miband2, CHAR_UUIDS.auth, null, (characteristicUUID, bytes) =>
    {
      string msg = "Received Serial[CHAR_UUIDS.auth]";
      if (bytes.Length >= 3)
      {
        byte[] code = { bytes[0], bytes[1], bytes[2] };
        if (IsEqual(code, AUTH_TYPES.received))
        {
          msg = "Authenticate Received";
        }
        else if (bytes.Length == 19 && IsEqual(code, AUTH_TYPES.requestcode))
        {
          msg = "Authenticate Request Code";
          List<byte> serial = new List<byte>();
          serial.AddRange(bytes);
          serial.RemoveRange(0, 3);
          if (serial.Count != 16)
          {
            txtMiBandStatus.text = "Unknown Request Code[" + Utility.Bytes2Hex(serial.ToArray()) + "]";
            msg = "";
          }
          else
          {
            try
            {
              byte[] key = Utility.Hex2Bytes(AuthKey);
              if (key.Length != 16)
              {
                txtMiBandStatus.text = "AuthKey must be 16 bytes[" + AuthKey + "]";
                msg = "";
              }
              else
              {
                byte[] iv = new byte[16];
                byte[] aes = AesEncrypt(serial.ToArray(), key, iv);
                List<byte> ls = new List<byte>();
                ls.Add(0x03);
                ls.Add(0x00);
                ls.AddRange(aes);
                byte[] authKey = ls.ToArray();
                txtMiBandStatus.text = "Writing Auth Code[" + Utility.Bytes2Hex(authKey) + "]";
                msg = "";
                BluetoothLEHardwareInterface.WriteCharacteristic(device, UUIDS.miband2, CHAR_UUIDS.auth, authKey, authKey.Length, false, (charUUID) =>
                {
                  txtMiBandStatus.text = "Writed Auth Code[" + Utility.Bytes2Hex(authKey) + "]";
                });
              }
            }
            catch(System.Exception E)
            {
              msg = "Unknown Exception[" + E.Message + "]";
            }
          }
        }
        else if (IsEqual(code, AUTH_TYPES.authfail))
        {
          msg = "Authenticate Failed";
          Invoke("SendAuth", 5); // retry after 5 seconds
        }
        else if (IsEqual(code, AUTH_TYPES.authed))
        {
          msg = "Authenticate Successful";

          bAuthed = true;
          StartCoroutine(delaySubscribeHeartrate(0.5f));
        }
        else
        {
          msg = "Authenticate Received Unknown Message";
        }
      }
      if (!string.IsNullOrEmpty(msg))
      {
        txtMiBandStatus.text = msg + "[" + bytes.Length.ToString() + "]" + Utility.Bytes2Hex(bytes);
      }
    });
  }
  public void SendAuth()
  {
    txtMiBandStatus.text = "Sending Auth Request[" + Utility.Bytes2Hex(NOTIFICATION_TYPES.auth) + "]";
    if (bConnected)
    {
      if (!bAuthed)
      {
        BluetoothLEHardwareInterface.WriteCharacteristic(device, UUIDS.miband2, CHAR_UUIDS.auth, NOTIFICATION_TYPES.auth, NOTIFICATION_TYPES.auth.Length, false, (charUUID) =>
        {
          txtMiBandStatus.text = "Sent Auth Request[" + Utility.Bytes2Hex(NOTIFICATION_TYPES.auth) + "][" + charUUID + "]";
        });
      }
    }
  }
  public void Unsubscribe()
  {
    txtMiBandStatus.text = "Unsubscribe";
    StartCoroutine(delayUnsubscribeHeartrate(1.0f));
  }
  string FullUUID(string uuid)
  {
    return "0000" + uuid + "-0000-1000-8000-00805F9B34FB";
  }

  bool IsEqual(string uuid1, string uuid2)
  {
    if (uuid1.Length == 4)
      uuid1 = FullUUID(uuid1);
    if (uuid2.Length == 4)
      uuid2 = FullUUID(uuid2);

    return (uuid1.ToUpper().Equals(uuid2.ToUpper()));
  }
  bool IsEqual(byte[] data1, byte[] data2)
  {
    if (data1.Length != data2.Length)
    {
      return false;
    }
    for (int i = 0; i < data1.Length; ++i)
    {
      if (data1[i] != data2[i])
      {
        return false;
      }
    }
    return true;
  }

  // AES CBC
  public static byte[] AesEncrypt(byte[] byteContnet, byte[] byteKEY, byte[] byteIV)
  {
    var aes = new System.Security.Cryptography.RijndaelManaged(); ;
    aes.Padding = System.Security.Cryptography.PaddingMode.None;
    aes.Mode = System.Security.Cryptography.CipherMode.CBC;

    aes.Key = byteKEY;
    aes.IV = byteIV;

    var crypto = aes.CreateEncryptor(byteKEY, byteIV);
    byte[] decrypted = crypto.TransformFinalBlock(byteContnet, 0, byteContnet.Length);
    crypto.Dispose();
    return decrypted;
  }
  public static byte[] Hex2Bytes(string src)
  {
    System.Collections.Generic.List<byte> ls = new System.Collections.Generic.List<byte>();
    char[] chs = src.ToCharArray();
    for (int i = 0; i < chs.Length; i += 2)
    {
      byte b = System.Convert.ToByte(new string(chs, i, 2), 16);
      ls.Add(b);
    }
    return ls.ToArray();
  }
  public static string Bytes2Hex(byte[] src)
  {
    System.Collections.Generic.List<char> ls = new System.Collections.Generic.List<char>();
    char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
    for (int i = 0; i < src.Length; i++)
    {
      ls.Add(hexDigits[src[i] >> 4 & 0x0f]);
      ls.Add(hexDigits[src[i] & 0x0f]);
    }
    return new string(ls.ToArray());
  }
}

以上代码适用于MiBand 3,4,5,不适用于MiBand 6,Auth验证方法应该有调整了

参考:
Unity3D BLE插件
蓝牙通信插件最新版Bluetooth LE for iOS tvOS and Android.unitypackage

MiBand AuthKey获取
Your Soul, Your Beats! —— 小米手环实时心率采集

GitHub项目
Mi Band 4/5 Heart Rate Monitor

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-03-15 22:58:26  更:2022-03-15 23:00:14 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/16 16:11:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码