QT for Android BLE Bluetooch QT BLE

news2024/12/26 11:31:33

小白式的介绍,很详细了,很多主要内容写在程序的注释里,慢慢看
下面是我的源码
https://download.csdn.net/download/qq_27620407/87464307
源码打不开的话可以试试下图的操作,之后电机确定,可能是加图标搞的,但是我没遇到过,需要的可以联系897741243@qq.com
在这里插入图片描述

pro文件添加

QT       += bluetooth

头文件

#include <QtBluetooth/qbluetoothlocaldevice.h> // 本地设备信息
#include <QBluetoothDeviceDiscoveryAgent>      // 设备搜寻
#include <QBluetoothDeviceInfo>                // 设备信息
#include <QLowEnergyController>                // 设备连接
#include <QLowEnergyService>                   // 数据接收、发送

操作BLE与普通蓝牙不同,普通蓝牙连接设备后直接收发就可以
BLE蓝牙需要连接设备后搜寻设备服务,连接服务,再查找特征值,根据不同特征值设计不同功能
后面篇章分为设备、服务、特征值

程序主要结构就是以上三个部分有固定的槽函数入口,入口内各有一套固定的信号和槽函数的组合
这里先提前写明H文件里的变量,不再根据功能划分了

private:
    Ui::MainWindow *ui;
    QBluetoothLocalDevice *m_plocalDevice;                   // 对本地蓝牙进行操作,如打开、关闭……
    QBluetoothDeviceDiscoveryAgent *m_pdeviceDiscoveryAgent; // 用于蓝牙设备搜寻
    QLowEnergyController *m_pcontrol;                        // 用于设备连接
    QLowEnergyService *m_pservice;                           // 用于数据接收、发送
    QLowEnergyCharacteristic m_readCharacteristic;           // "读" 服务特性
    QLowEnergyCharacteristic m_writeCharacteristic;          // "写" 服务特性
    QLowEnergyCharacteristic m_notifyCharacteristic;         // 通知 服务特性

    QLowEnergyService::WriteMode m_writeMode;                // "写"特性模式
    QLowEnergyDescriptor m_notificationDesc;                 // 用于存储BLE描述符信息

    QList<QBluetoothUuid> m_servicesUuid; // 服务uuid
    QList<DeviceInfo*> m_devices;         // 搜索到的蓝牙设备信息

    QStringList m_devicesNames;           // 搜索到的设备名称
    QStringList m_services;               // 服务列表

设备

首先将读取的设备信息归为一个类DeviceInfo

class DeviceInfo: public QObject
{
    Q_OBJECT

public:
    DeviceInfo(const QBluetoothDeviceInfo &device);
    void setDevice(const QBluetoothDeviceInfo &device);//设定当前通讯目标设备
    QString getName() const { return m_device.name(); }//获取设备名称
    QString getAddress() const;//获取MAC地址
    int getRssi() const;  //信号强度
    QBluetoothDeviceInfo getDevice() const;

signals:
    void deviceChanged();   //设定设备完成

private:
    QBluetoothDeviceInfo m_device;
};
DeviceInfo::DeviceInfo(const QBluetoothDeviceInfo &info):
    QObject(), m_device(info)
{
}
QBluetoothDeviceInfo DeviceInfo::getDevice() const
{
    return m_device;
}
//MAC地址
QString DeviceInfo::getAddress() const
{
#ifdef Q_OS_MAC
    // workaround for Core Bluetooth:
    return m_device.deviceUuid().toString();
#else
    return m_device.address().toString();
#endif
}
//信号强度
int DeviceInfo::getRssi() const
{
    return m_device.rssi();
}

void DeviceInfo::setDevice(const QBluetoothDeviceInfo &device)
{
    m_device = device;
    emit deviceChanged();
}

检测本地蓝牙并开始搜索

private: 
    QBluetoothLocalDevice *m_plocalDevice;                   // 对本地蓝牙进行操作,如打开、关闭……
	QBluetoothDeviceDiscoveryAgent *m_pdeviceDiscoveryAgent; // 用于蓝牙设备搜寻
	
    /*检测本地蓝牙状态*/
    m_plocalDevice = new QBluetoothLocalDevice(this);
    if(m_plocalDevice->hostMode() == QBluetoothLocalDevice::HostPoweredOff)
    {
        // 如果本地蓝牙处于关闭状态,则打开蓝牙
        m_plocalDevice->powerOn();
    }
    /*自动搜寻设备*/
    m_pdeviceDiscoveryAgent = new QBluetoothDeviceDiscoveryAgent(this);
    m_pdeviceDiscoveryAgent ->setLowEnergyDiscoveryTimeout(5000);//设置BLE的搜索时间
    connect(m_pdeviceDiscoveryAgent, SIGNAL(deviceDiscovered(const QBluetoothDeviceInfo&)),   //扫描发现新设备
            this, SLOT(on_addDevice(const QBluetoothDeviceInfo&)));
    connect(m_pdeviceDiscoveryAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)),    //错误
            this, SLOT(on_deviceScanError(QBluetoothDeviceDiscoveryAgent::Error)));
    connect(m_pdeviceDiscoveryAgent, SIGNAL(finished()),this, SLOT(on_scanFinished()));  //扫描完成

使用的信号

 void deviceDiscovered(const QBluetoothDeviceInfo &info); //发现新设备
 void error(QBluetoothDeviceDiscoveryAgent::Error error);  //查找设备时出错
 void finished();        //差找完成

对应的槽函数(需要自己写的)

//扫描到新设备,读取设备信息
void MainWindow::on_addDevice(const QBluetoothDeviceInfo &device)
{
    if (device.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration)  //筛选只要BLE设备
    {
        QString strDeviceName = device.name();      // 设备名称
        //        QBluetoothAddress addr = device.address();  // 设备地址
        //        qint16 iRssi = device.rssi();//信号强度

        //空名的不要
        if("" == strDeviceName) return;
        //同MAC的不要
        for(int i=0;i<m_devices.size();i++)
        {
            if(m_devices.at(i)->getAddress() == device.address().toString())
            {
                return;
            }
        }
        m_devicesNames.append(strDeviceName);    //记录搜索到的设备名
        DeviceInfo *dev = new DeviceInfo(device);   // 设备信息
        m_devices.append(dev);           //记录设备信息
        qDebug() << "Discovered LE Device name: " << device.name()<< " Address: "<< device.address().toString()<<QString::number(device.rssi());

        //发现并更新设备名称ComboBox
        QString label = QString("%1 \r\n%2").arg(device.name()).arg(device.address().toString());  //这里为了好看,记录设备名和设备地址
        QList<QListWidgetItem *> items = ui->Device_List->findItems(label, Qt::MatchExactly);      //显示备名和设备地址

        if (items.empty()) {
            QListWidgetItem *item = new QListWidgetItem(label);
            QBluetoothLocalDevice::Pairing pairingStatus = m_plocalDevice->pairingStatus(device.address());
            /* 蓝牙状态pairingStatus,Pairing枚举类型 0:Unpaired没配对 1:Paired配对但没授权 2:AuthorizedPaired配对且授权 */
            if (pairingStatus == QBluetoothLocalDevice::Paired || pairingStatus == QBluetoothLocalDevice::AuthorizedPaired )
            {
                item->setForeground(QColor(Qt::green));
            } else{
                item->setForeground(QColor(Qt::black));
            }
            ui->Device_List->addItem(item);
            //切换底色
            static bool Background=true;
            if(Background)
            {
                item->setBackground(QColor(Qt::darkGray));
            }else{
                item->setBackground(QColor(Qt::white));
            }
            Background=!Background;
        }
    }
}
//扫描出错
void MainWindow::on_deviceScanError(QBluetoothDeviceDiscoveryAgent::Error error)
{
    ui->textEdit->append(QString("错误:%1").arg(error));
}
//扫描完成后槽函数
void MainWindow::on_scanFinished()
{
    m_pdeviceDiscoveryAgent->stop();
    ui->textEdit->append("扫描完成");
}

手动扫描设备

    //手动扫描
    connect(ui->Scan_Device,&QPushButton::clicked,[=]{
        qDeleteAll(m_devices);                             //删除所有先前的设备信息
        m_devices.clear();                                 //删除所有先前的设备信息 
        m_pdeviceDiscoveryAgent->start();    //开始扫描
        ui->Device_List->clear();            //清除显示
        ui->textEdit->append("正在搜索...");  //提示信息
        ui->List_Box->setCurrentIndex(0);//开始扫描设备,切换到设备列表
    });

到这里第一组信号槽函数结束,完成BLE设备查找筛选并显示

连接设备

上一步将设备名显示在listwidget控件中,可以使用该控件的双击或者单独一个按钮,还有断开设备连接

m_pcontrol->disconnectFromDevice();//断开连接
m_pcontrol->connectToDevice(); //连接设备
    /* 双击listwidget的项目,触发连接蓝牙的槽 */
    connect(ui->Device_List, SIGNAL(itemActivated(QListWidgetItem*)),this, SLOT(connect_Device()));
    //连接设备
    connect(ui->Link_Device,&QPushButton::clicked,[=]{
        connect_Device();
    });
        //断开设备连接
    connect(ui->disLink_Device,&QPushButton::clicked,[=]{
        m_pcontrol->disconnectFromDevice();
        ui->Server_List->clear();
    });

itemActivated(QListWidgetItem*)是双击listwidget的信号
槽函数connect_Device

QLowEnergyController *m_pcontrol;                        // 用于设备连接
//连接BLE
void MainWindow::connect_Device()
{
    m_pdeviceDiscoveryAgent->stop();// 停止搜寻设备
    ui->Server_List->clear();       //重置服务信息
    ui->textEdit->append(QString::fromLocal8Bit("开始连接设备"));
    if(m_devices.isEmpty())            //如果没有连接设备提前终止
    {
        ui->textEdit->append(QString::fromLocal8Bit("没有设备"));
        return;
    }
    if(ui->Device_List->currentRow()!=-1)   //如果设备选取没有选取也提前终止,不然会闪退
    {
        DeviceInfo* currentDevice= m_devices[ui->Device_List->currentRow()];   //读取要读取服务的设备信息
        qDebug()<<currentDevice->getName()<<"----"<<currentDevice->getAddress();
        m_pcontrol = new QLowEnergyController(currentDevice->getDevice(), this);
        //每找到一个服务就会发出此信号
        connect(m_pcontrol, SIGNAL(error(QLowEnergyController::Error)),this, SLOT(on_controllerError(QLowEnergyController::Error)));
        connect(m_pcontrol, SIGNAL(disconnected()),this, SLOT(on_deviceDisconnected()));
        connect(m_pcontrol, SIGNAL(connected()),this, SLOT(on_deviceConnected()));
        connect(m_pcontrol, SIGNAL(discoveryFinished()),this, SLOT(on_serviceScanDone()));
        // 连接设备
        m_pcontrol->connectToDevice();
    }

}

这里要使用的信号

	void error(QLowEnergyController::Error newError);  //连接失败
	void connected();       //连接成功
    void disconnected();    //连接失败或断开连接
    void discoveryFinished();   //连接并找到服务

由于这里连接设备成功后我直接开始搜索服务,所以connected和discoveryFinished看起来有点重复
开始搜索服务包含在 on_serviceScanDone 函数内

后续需要的槽函数,需要自己写

//连接出错
void MainWindow::on_controllerError(QLowEnergyController::Error error)
{
    switch(error)
    {
    case QLowEnergyController::NoError:{ui->textEdit->append("NoError");break;}
    case QLowEnergyController::UnknownError:{ui->textEdit->append("UnknownError");break;}
    case QLowEnergyController::UnknownRemoteDeviceError:{ui->textEdit->append("UnknownRemoteDeviceError");break;}
    case QLowEnergyController::NetworkError:{ui->textEdit->append("NetworkError");break;}
    case QLowEnergyController::InvalidBluetoothAdapterError:{ui->textEdit->append("InvalidBluetoothAdapterError");break;}
    case QLowEnergyController::ConnectionError:{ui->textEdit->append("ConnectionError");break;}
    case QLowEnergyController::AdvertisingError:{ui->textEdit->append("AdvertisingError");break;}
    case QLowEnergyController::RemoteHostClosedError:{ui->textEdit->append("RemoteHostClosedError");break;}
    case QLowEnergyController::AuthorizationError:{ui->textEdit->append("AuthorizationError");break;}
    default: {ui->textEdit->append("Unknow error");break;}
    }
}
// 设备断开
void MainWindow::on_deviceDisconnected()
{
    ui->textEdit->append(tr("设备已断开连接!!!"));
    ui->List_Box->setCurrentIndex(0); //断开连接切换回设备列表
}
// 设备连接成功
void MainWindow::on_deviceConnected()
{
    ui->textEdit->append(tr("成功连接设备!!!"));
    m_servicesUuid.clear();
    m_services.clear();
    ui->List_Box->setCurrentIndex(1); //连接成功切换到服务器列表
    //发现服务Services
    m_pcontrol->discoverServices();
}

// 服务搜寻完毕,更新服务下拉框
void MainWindow::on_serviceScanDone()
{
    m_servicesUuid = m_pcontrol->services();
    if(m_servicesUuid.isEmpty())
    {
        ui->Server_List->addItem("无服务");
    }
    else
    {
        ui->Server_List->addItem(QString("服务数:%1").arg(m_servicesUuid.length()));
        for(int i=0;i<m_servicesUuid.length();i++)
        {
            QString UUID =m_servicesUuid.at(i).toString().remove('{').remove('}');//获取UUID  去除{} 生成的UUID是没有{}的
            int UUID_Num=UUID_Find.indexOf(UUID);   //查表UUID是哪类设备,没找也能用,下面这步为了好看
            if(UUID_Num!=-1)
            {
                ui->Server_List->addItem(QString("%1:%2\n%3").arg(i).arg(UUID).arg(UUID_Find.at(UUID_Num+1)));

            }else{
                ui->Server_List->addItem(QString("%1:%2\n%3").arg(i).arg(UUID).arg("Unknown"));
            }
            qDebug()<<"m_servicesUuid"<<m_servicesUuid.at(i).toString( );
        } 
    }
}

到这一步为止,完成连接设备并查找存在的服务

服务

连接服务使用的是UUID,这里从显示的控件中截取出UUID的字符格式,然后使用QUuid生成实际的UUID,再用m_pcontrol->createServiceObject函数连接服务

连接服务

    //双击连接服务器
    connect(ui->Server_List, SIGNAL(itemActivated(QListWidgetItem*)),this, SLOT(connect_Server(QListWidgetItem*)));
    //按钮连接服务
    connect(ui->Link_Server,&QPushButton::clicked,[=]{
        if(ui->Server_List->currentRow()!=-1)//先确认有设备被选择
        {
            connect_Server(ui->Server_List->currentItem());
        }
    });

槽函数 主要就是根据Qstring显示的UUID使用QUUID生成实际的UUID,重点在后面

void MainWindow::connect_Server(QListWidgetItem* Item)
{
    qDebug()<<"Item"<<Item;
    //连接服务使用的是UUID,这里从显示的控件中截取出UUID的字符格式,然后使用QUuid生成实际的UUID,再用m_pcontrol->createServiceObject函数连接服务
    if((Item->text().indexOf('\n')!=-1)&&(Item->text().indexOf(':')!=-1))
    {
        QString text = Item->text();
        ui->textEdit->append(QString("连接服务器"));
        //qDebug()<<"m_servicesUuid"<<m_servicesUuid.at(text.toInt());
        text = Item->text();
        text=text.left(text.indexOf('\n'));
        text.remove(0,text.lastIndexOf(':')+1);
        if(QUuid(text).toString()!="00000000-0000-0000-8000-000000000000")
        {
           update_currentService(QUuid(text));
        }else{
            ui->textEdit->append(QString("请选择服务器"));
        }
    }
}
//连接服务
void MainWindow::update_currentService(QBluetoothUuid servicesUuid)
{
    // 创建Service UUID所表示的服务实例
    m_pservice = m_pcontrol->createServiceObject(servicesUuid, this);
    connect(m_pservice, SIGNAL(stateChanged(QLowEnergyService::ServiceState)),this, SLOT(on_serviceStateChanged(QLowEnergyService::ServiceState)));
    connect(m_pservice, SIGNAL(characteristicChanged(QLowEnergyCharacteristic,QByteArray)),this, SLOT(on_characteristicChanged(QLowEnergyCharacteristic,QByteArray)));
    connect(m_pservice, SIGNAL(characteristicRead(QLowEnergyCharacteristic,QByteArray)),this, SLOT(on_characteristicRead(QLowEnergyCharacteristic,QByteArray)));
    connect(m_pservice, SIGNAL(characteristicWritten(QLowEnergyCharacteristic,QByteArray)),this, SLOT(on_characteristicWrite(QLowEnergyCharacteristic,QByteArray)));
    connect(m_pservice, SIGNAL(error(QLowEnergyService::ServiceError)),this, SLOT(on_serviceError(QLowEnergyService::ServiceError)));
    if(m_pservice->state() == QLowEnergyService::DiscoveryRequired)
    {
        ui->textEdit->append("查找特性");
        m_pservice->discoverDetails();//查找特性
    }
    else
    {
        ui->textEdit->append("未同步");
        searchCharacteristic();
    }
}

这里的信号槽函数最多
直接进入特征值详解,

特征值

对这个我不是很清楚,现在是一知半解的用着

void stateChanged(QLowEnergyService::ServiceState newState);        //服务被发现
void characteristicChanged(const QLowEnergyCharacteristic &info,const QByteArray &value); //Read服务值和Notify服务值都会触发 
void characteristicRead(const QLowEnergyCharacteristic &info, const QByteArray &value);//read服务,即app收到read服务值的信息时会触发这个,相当于串口接收
void characteristicWritten(const QLowEnergyCharacteristic &info,const QByteArray &value);//write写完后会触发
void error(QLowEnergyService::ServiceError error);

经过update_currentService 函数后,需要的槽函数

void MainWindow::searchCharacteristic()
{
    if(m_pservice)
    {
        foreach (QLowEnergyCharacteristic c, m_pservice->characteristics() )
        {
            ui->List_Box->setCurrentIndex(2);
            //ui->Service_List->addItem(c.uuid().toString());
            //c.properties()   //类型
            //QLowEnergyCharacteristic::PropertyType
            if(c.isValid())
            {
                ui->textEdit->append(QString(c.uuid().toString()));
                if (((c.properties() == QLowEnergyCharacteristic::WriteNoResponse) ||
                     (c.properties() == QLowEnergyCharacteristic::Write)))
                {
                    m_writeCharacteristic = c;
                    if((c.properties() & QLowEnergyCharacteristic::WriteNoResponse))
                    {
                        m_writeMode = QLowEnergyService::WriteWithoutResponse;
                        ui->textEdit->append("WriteWithoutResponse");
                        ui->Service_List->addItem(QString("%1\n%2").arg(c.uuid().toString()).arg("WriteWithoutResponse"));
                    }
                    else
                    {
                        m_writeMode = QLowEnergyService::WriteWithResponse;
                        ui->textEdit->append("WriteWithResponse");
                        ui->Service_List->addItem(QString("%1\n%2").arg(c.uuid().toString()).arg("WriteWithResponse"));
                    }
                }else{
                    if ((c.properties() == QLowEnergyCharacteristic::Read))
                    {
                        m_readCharacteristic = c;
                        ui->textEdit->append("Read");
                        ui->Service_List->addItem(QString("%1\n%2").arg(c.uuid().toString()).arg("Read"));
                    }else{
                        if ((c.properties() == QLowEnergyCharacteristic::Notify))
                        {
                            m_notifyCharacteristic = c;
                            ui->textEdit->append("Notify");
                            ui->Service_List->addItem(QString("%1\n%2").arg(c.uuid().toString()).arg("Notify"));
                        }
                    }
                }

                m_notificationDesc = c.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
                if (m_notificationDesc.isValid())
                {
                    m_pservice->writeDescriptor(m_notificationDesc, QByteArray::fromHex("0100"));
                }
            }
        }
    }
}

void MainWindow::dataReceived(QByteArray value)
{
    ui->Re_Data->setText(QString("dataReceived:%1").arg(QString(value)));
    ui->Re_Data->append(QString("HEX:%1").arg(QString(value.toHex().toUpper())));
}
void MainWindow::Notify_Data(QByteArray value)
{
    ui->NOTIFY->setText(value.toHex().toUpper());
}
void MainWindow::write(const QByteArray &data)
{
    if(m_pservice && m_writeCharacteristic.isValid())
    {
        m_pservice->writeCharacteristic(m_writeCharacteristic, data, m_writeMode);
    }else{
        qDebug()<<"发送失败";
    }
}

// 服务被发现
void MainWindow::on_serviceStateChanged(QLowEnergyService::ServiceState s)
{

    if (s == QLowEnergyService::ServiceDiscovered)
    {
        ui->textEdit->append("服务被发现");
        searchCharacteristic();
    }
}
void MainWindow::on_characteristicChanged(const QLowEnergyCharacteristic &c,
                              const QByteArray &value)
{
    qDebug() << "Characteristic Changed: " << value;
    //qDebug() <<c.name()<<"|"<<c.uuid()<<"|"<<c.value()<<"|"<<c.handle()<<"|"<<c.isValid()<<"|"<<c.properties();
    //qDebug() << "uuid: " << c.uuid();
    //ui->textEdit->append(QString("Characteristic Changed: %1 %2").arg(value.toInt()).arg(c.uuid().toString()));
    if(c.properties()==QLowEnergyCharacteristic::Notify)
    {
        Notify_Data(value);
    }else{
        if(c.properties()==QLowEnergyCharacteristic::Read)
        {
            dataReceived(value);
        }else{
            ui->textEdit->append("Unkown characteristicChanged");
        }
    }


}

void MainWindow::on_characteristicRead(const QLowEnergyCharacteristic &c,
                           const QByteArray &value)
{
    qDebug() << "Characteristic Read: " << value.toHex();
    qDebug() << "uuid: " << c.uuid();
    ui->textEdit->append(QString("Characteristic Read:|%1|%2").arg(QString(value.toHex())).arg(c.uuid().toString()));
    dataReceived(value);
}

void MainWindow::on_characteristicWrite(const QLowEnergyCharacteristic &c,
                            const QByteArray &value)
{
    qDebug() << "Characteristic Written: " << value;
    qDebug() << "uuid: " << c.uuid();
}
void MainWindow::on_serviceError(QLowEnergyService::ServiceError e)
{
    ui->textEdit->append(QString("serviceError %1").arg(e));
}

重点是searchCharacteristic函数 对查找出来的特征值分类,并分配相应的的 QLowEnergyCharacteristic
QLowEnergyCharacteristic是后续服务特性的主要部分

    QLowEnergyCharacteristic m_readCharacteristic;           // "读" 服务特性
    QLowEnergyCharacteristic m_writeCharacteristic;          // "写" 服务特性
    QLowEnergyCharacteristic m_notifyCharacteristic;         // 通知 服务特性

分类依据就是 c.properties() 这里返回的是个枚举,可以直接跳转查看,就是QLowEnergyCharacteristic对象中的PropertyType

    enum PropertyType {
        Unknown = 0x00,
        Broadcasting = 0x01,
        Read = 0x02,
        WriteNoResponse = 0x04,
        Write = 0x08,
        Notify = 0x10,
        Indicate = 0x20,
        WriteSigned = 0x40,
        ExtendedProperty = 0x80
    };

常用的就是

QLowEnergyCharacteristic::WriteNoResponse
QLowEnergyCharacteristic::Write
QLowEnergyCharacteristic::Read
QLowEnergyCharacteristic::Notify

dataReceived和Notify_Data用于显示收到的数据和通知数据,数据包含在QByteArray value

write是app往外发数据用的,这个很简单,和串口发送一样

    connect(ui->btn_Send,&QPushButton::clicked,[=]{
        QByteArray data;
        if(ui->Send_Hex->checkState()==Qt::Checked)
        {
            data =  QByteArray::fromHex(ui->Re_Data_2->toPlainText().toLatin1());
        }
        else
        {
            data = QByteArray(ui->Re_Data_2->toPlainText().toLatin1());
        }
        write(data);
    });

on_characteristicChanged和on_characteristicRead用起来会有些重复

现在就简单的能用,详细的部分后续再补充

常见的UUID

QStringList UUID_Find=
{// Sample Services.
"0000180d-0000-1000-8000-00805f9b34fb", "Heart Rate Service"	,
"0000180a-0000-1000-8000-00805f9b34fb", "Device Information Service"	,
// Sample Characteristics.
"00002a37-0000-1000-8000-00805f9b34fb", "Heart Rate Measurement"	,
"00002a29-0000-1000-8000-00805f9b34fb", "Manufacturer Name String"	,

// GATT Services
"00001800-0000-1000-8000-00805f9b34fb", "Generic Access"	,
"00001801-0000-1000-8000-00805f9b34fb", "Generic Attribute"	,


// GATT Declarations
"00002800-0000-1000-8000-00805f9b34fb", "Primary Service"	,
"00002801-0000-1000-8000-00805f9b34fb", "Secondary Service"	,
"00002802-0000-1000-8000-00805f9b34fb", "Include"	,
"00002803-0000-1000-8000-00805f9b34fb", "Characteristic"	,

// GATT Descriptors
"00002900-0000-1000-8000-00805f9b34fb", "Characteristic Extended Properties"	,
"00002901-0000-1000-8000-00805f9b34fb", "Characteristic User Description"	,
"00002902-0000-1000-8000-00805f9b34fb", "Client Characteristic Configuration"	,
"00002903-0000-1000-8000-00805f9b34fb", "Server Characteristic Configuration"	,
"00002904-0000-1000-8000-00805f9b34fb", "Characteristic Presentation Format"	,
"00002905-0000-1000-8000-00805f9b34fb", "Characteristic Aggregate Format"	,
"00002906-0000-1000-8000-00805f9b34fb", "Valid Range"	,
"00002907-0000-1000-8000-00805f9b34fb", "External Report Reference Descriptor"	,
"00002908-0000-1000-8000-00805f9b34fb", "Report Reference Descriptor"	,

// GATT Characteristics
"00002a00-0000-1000-8000-00805f9b34fb", "Device Name"	,
"00002a01-0000-1000-8000-00805f9b34fb", "Appearance"	,
"00002a02-0000-1000-8000-00805f9b34fb", "Peripheral Privacy Flag"	,
"00002a03-0000-1000-8000-00805f9b34fb", "Reconnection Address"	,
"00002a04-0000-1000-8000-00805f9b34fb", "PPCP"	,
"00002a05-0000-1000-8000-00805f9b34fb", "Service Changed"	,

// GATT Service UUIDs
"00001802-0000-1000-8000-00805f9b34fb", "Immediate Alert"	,
"00001803-0000-1000-8000-00805f9b34fb", "Link Loss"	,
"00001804-0000-1000-8000-00805f9b34fb", "Tx Power"	,
"00001805-0000-1000-8000-00805f9b34fb", "Current Time Service"	,
"00001806-0000-1000-8000-00805f9b34fb", "Reference Time Update Service"	,
"00001807-0000-1000-8000-00805f9b34fb", "Next DST Change Service"	,
"00001808-0000-1000-8000-00805f9b34fb", "Glucose"	,
"00001809-0000-1000-8000-00805f9b34fb", "Health Thermometer"	,
"0000180a-0000-1000-8000-00805f9b34fb", "Device Information"	,
"0000180b-0000-1000-8000-00805f9b34fb", "Network Availability"	,
"0000180d-0000-1000-8000-00805f9b34fb", "Heart Rate"	,
"0000180e-0000-1000-8000-00805f9b34fb", "Phone Alert Status Service"	,
"0000180f-0000-1000-8000-00805f9b34fb", "Battery Service"	,
"00001810-0000-1000-8000-00805f9b34fb", "Blood Pressure"	,
"00001811-0000-1000-8000-00805f9b34fb", "Alert Notification Service"	,
"00001812-0000-1000-8000-00805f9b34fb", "Human Interface Device"	,
"00001813-0000-1000-8000-00805f9b34fb", "Scan Parameters"	,
"00001814-0000-1000-8000-00805f9b34fb", "Running Speed and Cadence"	,
"00001816-0000-1000-8000-00805f9b34fb", "Cycling Speed and Cadence"	,
"00001818-0000-1000-8000-00805f9b34fb", "Cycling Power"	,
"00001819-0000-1000-8000-00805f9b34fb", "Location and Navigation"	,

// GATT Characteristic UUIDs
"00002a06-0000-1000-8000-00805f9b34fb", "Alert Level"	,
"00002a07-0000-1000-8000-00805f9b34fb", "Tx Power Level"	,
"00002a08-0000-1000-8000-00805f9b34fb", "Date Time"	,
"00002a09-0000-1000-8000-00805f9b34fb", "Day of Week"	,
"00002a0a-0000-1000-8000-00805f9b34fb", "Day Date Time"	,
"00002a0c-0000-1000-8000-00805f9b34fb", "Exact Time 256"	,
"00002a0d-0000-1000-8000-00805f9b34fb", "DST Offset"	,
"00002a0e-0000-1000-8000-00805f9b34fb", "Time Zone"	,
"00002a0f-0000-1000-8000-00805f9b34fb", "Local Time Information"	,
"00002a11-0000-1000-8000-00805f9b34fb", "Time with DST"	,
"00002a12-0000-1000-8000-00805f9b34fb", "Time Accuracy"	,
"00002a13-0000-1000-8000-00805f9b34fb", "Time Source"	,
"00002a14-0000-1000-8000-00805f9b34fb", "Reference Time Information"	,
"00002a16-0000-1000-8000-00805f9b34fb", "Time Update Control Point"	,
"00002a17-0000-1000-8000-00805f9b34fb", "Time Update State"	,
"00002a18-0000-1000-8000-00805f9b34fb", "Glucose Measurement"	,
"00002a19-0000-1000-8000-00805f9b34fb", "Battery Level"	,
"00002a1c-0000-1000-8000-00805f9b34fb", "Temperature Measurement"	,
"00002a1d-0000-1000-8000-00805f9b34fb", "Temperature Type"	,
"00002a1e-0000-1000-8000-00805f9b34fb", "Intermediate Temperature"	,
"00002a21-0000-1000-8000-00805f9b34fb", "Measurement Interval"	,
"00002a22-0000-1000-8000-00805f9b34fb", "Boot Keyboard Input Report"	,
"00002a23-0000-1000-8000-00805f9b34fb", "System ID"	,
"00002a24-0000-1000-8000-00805f9b34fb", "Model Number String"	,
"00002a25-0000-1000-8000-00805f9b34fb", "Serial Number String"	,
"00002a26-0000-1000-8000-00805f9b34fb", "Firmware Revision String"	,
"00002a27-0000-1000-8000-00805f9b34fb", "Hardware Revision String"	,
"00002a28-0000-1000-8000-00805f9b34fb", "Software Revision String"	,
"00002a29-0000-1000-8000-00805f9b34fb", "Manufacturer Name String"	,
"00002a2a-0000-1000-8000-00805f9b34fb", "IEEE 11073-20601 Regulatory Certification Data List"	,
"00002a2b-0000-1000-8000-00805f9b34fb", "Current Time"	,
"00002a31-0000-1000-8000-00805f9b34fb", "Scan Refresh"	,
"00002a32-0000-1000-8000-00805f9b34fb", "Boot Keyboard Output Report"	,
"00002a33-0000-1000-8000-00805f9b34fb", "Boot Mouse Input Report"	,
"00002a34-0000-1000-8000-00805f9b34fb", "Glucose Measurement Context"	,
"00002a35-0000-1000-8000-00805f9b34fb", "Blood Pressure Measurement"	,
"00002a36-0000-1000-8000-00805f9b34fb", "Intermediate Cuff Pressure"	,
"00002a37-0000-1000-8000-00805f9b34fb", "Heart Rate Measurement"	,
"00002a38-0000-1000-8000-00805f9b34fb", "Body Sensor Location"	,
"00002a39-0000-1000-8000-00805f9b34fb", "Heart Rate Control Point"	,
"00002a3e-0000-1000-8000-00805f9b34fb", "Network Availability"	,
"00002a3f-0000-1000-8000-00805f9b34fb", "Alert Status"	,
"00002a40-0000-1000-8000-00805f9b34fb", "Ringer Control Point"	,
"00002a41-0000-1000-8000-00805f9b34fb", "Ringer Setting"	,
"00002a42-0000-1000-8000-00805f9b34fb", "Alert Category ID Bit Mask"	,
"00002a43-0000-1000-8000-00805f9b34fb", "Alert Category ID"	,
"00002a44-0000-1000-8000-00805f9b34fb", "Alert Notification Control Point"	,
"00002a45-0000-1000-8000-00805f9b34fb", "Unread Alert Status"	,
"00002a46-0000-1000-8000-00805f9b34fb", "New Alert"	,
"00002a47-0000-1000-8000-00805f9b34fb", "Supported New Alert Category"	,
"00002a48-0000-1000-8000-00805f9b34fb", "Supported Unread Alert Category"	,
"00002a49-0000-1000-8000-00805f9b34fb", "Blood Pressure Feature"	,
"00002a4a-0000-1000-8000-00805f9b34fb", "HID Information"	,
"00002a4b-0000-1000-8000-00805f9b34fb", "Report Map"	,
"00002a4c-0000-1000-8000-00805f9b34fb", "HID Control Point"	,
"00002a4d-0000-1000-8000-00805f9b34fb", "Report"	,
"00002a4e-0000-1000-8000-00805f9b34fb", "Protocol Mode"	,
"00002a4f-0000-1000-8000-00805f9b34fb", "Scan Interval Window"	,
"00002a50-0000-1000-8000-00805f9b34fb", "PnP ID"	,
"00002a51-0000-1000-8000-00805f9b34fb", "Glucose Feature"	,
"00002a52-0000-1000-8000-00805f9b34fb", "Record Access Control Point"	,
"00002a53-0000-1000-8000-00805f9b34fb", "RSC Measurement"	,
"00002a54-0000-1000-8000-00805f9b34fb", "RSC Feature"	,
"00002a55-0000-1000-8000-00805f9b34fb", "SC Control Point"	,
"00002a5b-0000-1000-8000-00805f9b34fb", "CSC Measurement"	,
"00002a5c-0000-1000-8000-00805f9b34fb", "CSC Feature"	,
"00002a5d-0000-1000-8000-00805f9b34fb", "Sensor Location"	,
"00002a63-0000-1000-8000-00805f9b34fb", "Cycling Power Measurement"	,
"00002a64-0000-1000-8000-00805f9b34fb", "Cycling Power Vector"	,
"00002a65-0000-1000-8000-00805f9b34fb", "Cycling Power Feature"	,
"00002a66-0000-1000-8000-00805f9b34fb", "Cycling Power Control Point"	,
"00002a67-0000-1000-8000-00805f9b34fb", "Location and Speed"	,
"00002a68-0000-1000-8000-00805f9b34fb", "Navigation"	,
"00002a69-0000-1000-8000-00805f9b34fb", "Position Quality"	,
"00002a6a-0000-1000-8000-00805f9b34fb", "LN Feature"	,
"00002a6b-0000-1000-8000-00805f9b34fb", "LN Control Point"	};

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

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

相关文章

自监督表征预训练之掩码图像建模

自监督表征预训练之掩码图像建模 前言 目前&#xff0c;在计算机视觉领域&#xff0c;自监督表征预训练有两个主流方向&#xff0c;分别是对比学习&#xff08;contrastive learning&#xff09;和掩码图像建模&#xff08;masked image modeling&#xff09;。两个方向在近几…

CLEVE:事件抽取的对比预训练

CLEVE: Contrastive Pre-training for Event Extraction 论文&#xff1a;CLEVE: Contrastive Pre-training for Event Extraction (arxiv.org) 代码&#xff1a;THU-KEG/CLEVE: Source code for ACL 2021 paper “CLEVE: Contrastive Pre-training for Event Extraction” (g…

初探Upgrade内存马(内存马系列篇六)

写在前面 前面讲解了一个特殊的Tomcat内存马-Executor内存马&#xff0c;这篇同样是一个特殊&#xff0c;可以不被检测到的内存马-Upgrade内存马。 这篇就是内存马系列文章的第六篇了。 前置 在阅读这篇文章之前&#xff0c;同样需要对Tomcat的架构&#xff0c;和初始化流程…

秒懂算法 | 莫队算法

01、基础莫队算法 莫队算法 = 离线 + 暴力 + 分块。 “离线”和“在线”的概念。在线是交互式的,一问一答;如果前面的答案用于后面的提问,称为“强制在线”。离线是非交互的,一次性读取所有问题,然后一起回答,"记录所有步,回头再做”。 基础的莫队算法是一种离线…

[Java安全]—Shiro回显内存马注入

文章目录前言流程分析寻找response流程分析获取Http11Processor获取AbstractProtocol获取Connector获取WebappClassLoaderHeader 长度限制绕过1、反射修改maxHeaderSize2、自定义ClassLoader加载Body数据后记参考前言 接上篇[Java安全]—Tomcat反序列化注入回显内存马_&#x…

【基于混合激活残差块:超分】

SRNHARB: A deep light-weight image super resolution network using hybrid activation residual blocks &#xff08;SRNHARB&#xff1a;一种基于混合激活残差块的深度轻量图像超分辨率网络&#xff09; 在所有基于图像的应用中&#xff0c;特别是在计算机视觉应用中&…

软件测试期末

考原题就是爽 软件测试技术 知识点整理 https://wenku.baidu.com/view/524c900f4b2fb4daa58da0116c175f0e7cd11913.html 关键知识点 https://www.cnblogs.com/whylaughing/category/813559.html?page1 边界值法不选择无效数据 边界值分析法的基本思想 选取正好等于&am…

PageObject设计模式,在selenium自动化测试中的运用

PageObject设计模式 Web自动化测试框架&#xff08;WebTestFramework&#xff09;是基于Selenium框架且采用PageObject设计模式进行二次开发形成的框架。web测试时,建议强烈推荐使用_谷歌或_火狐浏览器。PageObject设计模式&#xff1a;是将某个页面的所有"元素&#xff…

【网络原理3】TCP连接管理

TCP这种传输层协议必须是有连接的。连接管理&#xff0c;就是TCP当中管理如何建立连接、如何断开连接的方式。 目录 TCP建立连接的方式(三次握手) 合并两次连接之后变成"三次握手" 站在具体发送什么报文的视角&#xff0c;理解三次握手 发送报文之前 第一步:客…

Git - 在主分支上创建分支并提交代码

拉取最新代码 因为当前在 master 分支下&#xff0c;你必须拉取最新代码&#xff0c;保证当前代码与线上同步&#xff08;最新&#xff09;&#xff0c;执行以下命令&#xff1a; git pull origin master创建分支 目前我们在 master 主分支上&#xff0c;需要执行以下命令&…

【Unity】[入门tips与通用性原则] 一些经验技巧和更好地写出简洁易懂的程序的原则方法

本文将持续间断更新 本文主要面向初级程序员&#xff0c;为了方便Unity开发&#xff0c;有些快捷键的方式和一些通用性的技巧tips等会在这篇博客内持续更新&#xff0c;欢迎点赞收藏 快捷键 Ctrl S &#xff1b; 快捷保存&#xff01;闲着没事就来两下&#xff01;CtrlShif…

CMake基础使用和实战详解

CMake基础使用和实战详解一、CMake简介1.1、cmake 的特点1.2、注意1.3、使用建议二、安装 cmake三、CMake的简单使用3.1、准备工作3.2、开始构建3.3、解释CMakeLists.txt的内容3.4、基本语法规则四、更像样的CMake工程4.1、准备工作4.2、构建4.3、语法解释4.4、修改保存目标二进…

水文监测场景的数据通信规约解析和落地实践

[小 迪 导 读]&#xff1a;江苏云上需要通过云平台接入水文设备来实现水文数据的采集、存储、显示、控制、报警及传输等综合功能。企业介绍江苏云上智联物联科技有限公司是专业从事物联网相关产品与解决方案服务的高科技公司&#xff0c;总部位于美丽的江苏无锡。公司遵循“智联…

linux高级命令之死锁

死锁学习目标能够知道产生死锁的原因1. 死锁的概念死锁: 一直等待对方释放锁的情景就是死锁为了更好的理解死锁&#xff0c;来看一个现实生活的效果图:说明:现实社会中&#xff0c;男女双方一直等待对方先道歉的这种行为就好比是死锁。死锁的结果会造成应用程序的停止响应&…

面试官:你是怎样理解Fiber的

hello&#xff0c;这里是潇晨&#xff0c;今天我们来聊一聊Fiber。不知道大家面试的时候有没有遇到过和react Fiber相关的问题呢&#xff0c;这一类问题比较开放&#xff0c;但也是考察对react源码理解深度的问题&#xff0c;如果面试高级前端岗&#xff0c;恰巧你平时用的是re…

细谈JavaWeb中的Request和Response

文章目录1&#xff0c;Request和Response的概述2&#xff0c;Request对象2.1 Request继承体系2.2 Request获取请求数据2.2.1 获取请求行数据2.2.2 获取请求头数据2.2.3 获取请求体数据2.2.4 获取请求参数的通用方式2.4 请求参数中文乱码问题2.4.1 POST请求解决方案2.4.2 GET请求…

MySQL使用C语言连接

文章目录MySQL使用C语言连接引入库下载库文件在项目中使用库使用库连接数据库下发SQL请求获取查询结果MySQL使用C语言连接 引入库 要使用C语言连接MySQL&#xff0c;需要使用MySQL官网提供的库。 下载库文件 下载库文件 首先&#xff0c;进入MySQL官网&#xff0c;选择DEVEL…

Java代码使用最小二乘法实现线性回归预测

最小二乘法简介最小二乘法是一种在误差估计、不确定度、系统辨识及预测、预报等数据处理诸多学科领域得到广泛应用的数学工具。它通过最小化误差&#xff08;真实目标对象与拟合目标对象的差&#xff09;的平方和寻找数据的最佳函数匹配。利用最小二乘法可以简便地求得未知的数…

如何写一个 things3 client

Things3[1] 是一款苹果生态内的任务管理软件&#xff0c;是一家德国公司做的&#xff0c;非常好用。我前后尝试了众多任务管理软件&#xff0c;最终选定 things3&#xff0c;以后有机会会写文章介绍我是如何用 things3 来管理我的日常任务。本文主要介绍欧神写的 tli[2] 工具来…

3D沉浸式体验开发技巧【Three.js】

在本文中&#xff0c;我们将看看如何使用 Three.js 创建一个充满后期效果和微交互的迷你城市。 推荐&#xff1a;将 NSDT场景编辑器 加入你的3D开发工具链。 1、背景介绍 我是一个游戏爱好者。 我一直梦想创建一个交互式迷你城市&#xff0c;使用饱和的颜色&#xff0c;类似于…