使用bluez堆栈如何实现可扫描的BLE信标?

huangapple go评论61阅读模式
英文:

How can a scannable BLE beacon be implemented using the bluez stack?

问题

我试图实现一个允许请求附加信息的BLE信标。

到目前为止,我的理解是,在BLE中,设备可以广播广告数据包。广告数据包可以指示设备是可扫描的。这意味着客户端可以向信标发送扫描请求。然后,信标可以发送扫描响应,其中包含附加信息。

因此,交换的数据包将如下所示:ADV_SCAN_IND -> SCAN_REQ -> SCAN_RSP。

我试图理解信标的实现应该如何行为。这是由适配器实现的吗(我需要预先指定要在扫描响应中发送的数据)?还是信标应该监听SCAN_REQ数据包,并在看到一个时广播SCAN_RSP?

我一直在寻找在Rust或Go中使用的库,但是使用这些语言的bluez开发BLE外设的支持似乎不足。

我可以接受任何编程语言/库的答案,只要它在Linux上运行即可。

到目前为止,我使用Rust中的bluer来实现最接近的效果。

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let session = bluer::Session::new().await?;
    let adapter = session.default_adapter().await?;
    adapter.set_powered(true).await?;

    println!(
        "Advertising on Bluetooth adapter {} with address {}",
        adapter.name(),
        adapter.address().await?
    );

    let mut data = BTreeMap::new();
    data.insert(0, vec![1, 2, 3]);

    let le_advertisement = Advertisement {
        advertisement_type: bluer::adv::Type::Broadcast,
        local_name: Some("le_advertise".to_string()),
        advertisting_data: data,
        ..Default::default()
    };

    println!("{:?}", &le_advertisement);
    let handle = adapter.advertise(le_advertisement).await?;

    std::thread::sleep(std::time::Duration::from_secs(30));

    println!("Removing advertisement");
    drop(handle);
    Ok(())
}

这可以用于广播广告。我可以在我的手机上使用nRF Connect看到它。

然而,我找不到响应扫描请求或指示信标可扫描的方法。

英文:

I'm trying to implement a BLE beacon which allows for additional information to be requested.

My understanding so far is that in BLE, a device can broadcast advertisement packets. An advertisement packet can indicate that the device is scannable. This means that a client can send a scan request to the beacon. A scan response can then be sent by the beacon, containing additional information.

So the exchanged packets would look like this: ADV_SCAN_IND -> SCAN_REQ -> SCAN_RSP.

I'm trying to understand how the beacon implementation should behave. Is this something implemented by the adapter (I would have to specify upfront the data to send back in a scan response)? Or should the beacon listen for SCAN_REQ packets and broadcast a SCAN_RSP when it sees one?

I've been looking for libraries to use in Rust or Go, however support for developing a BLE peripheral seems to be lacking when using bluez in those languages.

I'm fine with answers in any programming language / library, as long as it works on Linux

The closes I've gotten so far is using bluer for Rust.

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let session = bluer::Session::new().await?;
    let adapter = session.default_adapter().await?;
    adapter.set_powered(true).await?;

    println!(
        "Advertising on Bluetooth adapter {} with address {}",
        adapter.name(),
        adapter.address().await?
    );

    let mut data = BTreeMap::new();
    data.insert(0, vec![1, 2, 3]);

    let le_advertisement = Advertisement {
        advertisement_type: bluer::adv::Type::Broadcast,
        local_name: Some("le_advertise".to_string()),
        advertisting_data: data,
        ..Default::default()
    };

    println!("{:?}", &le_advertisement);
    let handle = adapter.advertise(le_advertisement).await?;

    std::thread::sleep(std::time::Duration::from_secs(30));

    println!("Removing advertisement");
    drop(handle);
    Ok(())
}

This works for broadcasting an advertisement. I can see it on my phone using nRF Connect.

However I cannot find a way to respond to scan requests, nor a way to indicate that the beacon can be scanned.

答案1

得分: 2

从理论部分开始,你对扫描响应的理解是正确的。来自蓝牙SIG的官方定义是(蓝牙核心规范 v5.4,Vol 1,Part A,section 4.2.2.2):

广告设备可能会收到来自监听设备的扫描请求,以从广告设备获取额外的用户数据。扫描响应是由广告设备发送给发出扫描请求的设备。

并且在这里也提到了(蓝牙核心规范 v5.4,Vol 1,Part A,section 3.3.2.2.2):

由广告设备发送的一些广告事件允许监听设备(扫描器)在接收到广告数据包的相同广告PHY信道上同时发送扫描请求或连接请求数据包。广告设备可以在相同广告事件内再次在相同广告PHY信道上发送扫描响应数据包。

通常在设备开始广告之前就设置了扫描响应,然而,允许在设备已经开始广告时设置或更改扫描响应。这在蓝牙规范 v5.4,Vol 4,Part E,7.8.8 LES Set Scan Response Data 命令中提到:

如果当前已启用广告,则控制器将在后续广告事件中使用新数据。如果在发出此命令时广告事件正在进行中,控制器可以对该事件使用旧数据或新数据。如果当前已禁用广告,则控制器将保留数据,并在启用广告后使用一次。

默认的 Scan_Response_Data_Length 应为零,默认的 Scan_Response_Data 应为 31 个零八位字节。

至于实际部分,你可以在Linux上使用btmgmt工具轻松发送扫描报告,并使用以下命令:

sudo btmgmt add-adv -d 02010606084142434400 -s 080954657374204C45

其中 -d 选项用于设置广告数据,-s 用于设置扫描响应数据。btmgmt add-adv 的完整选项列表如下:

Usage: add-adv [options] <instance_id>

Options:
	 -u, --uuid <uuid>         Service UUID
	 -d, --adv-data <data>     Advertising Data bytes
	 -s, --scan-rsp <data>     Scan Response Data bytes
	 -t, --timeout <timeout>   Timeout in seconds
	 -D, --duration <duration> Duration in seconds
	 -P, --phy <phy>           Phy type, Specify 1M/2M/CODED
	 -c, --connectable         "connectable" flag
	 -g, --general-discov      "general-discoverable" flag
	 -l, --limited-discov      "limited-discoverable" flag
	 -n, --scan-rsp-local-name "local-name" flag
	 -a, --scan-rsp-appearance "appearance" flag
	 -m, --managed-flags       "managed-flags" flag
	 -p, --tx-power            "tx-power" flag
e.g.:
	add-adv -u 180d -u 180f -d 080954657374204C45 1

广告中的BLE数据解码如下(基于指定的编号文档):

  • 第一个字节 = 长度(n 个字节)
  • 第二个字节 = 类型
  • n-1 个字节 = 实际数据

所以我添加的广告数据的含义是:

  • 02 长度(2 字节)

  • 01 类型(标志)

  • 06 标志 - 02 && 04 LE General Discoverable && BR/EDR Not supported

  • 06 长度(6 字节)

  • 08 类型(缩短的本地名称)

  • 4142434400(ABCD)

扫描响应数据的含义是:

  • 08 长度(8 字节)
  • 09 类型(完整的本地名称)
  • 54657374204C45(Test LE)

一些更有用的链接:

英文:

Starting with the theoretical part, you are right about your understanding of the scan response. The official definition from the Bluetooth SIG is (Bluetooth Core Specification v5.4, Vol 1, Part A, section 4.2.2.2):-

> Advertising devices may receive scan requests from listening devices
> in order to get additional user data from the advertising device. Scan
> responses are sent by the advertising device to the device making the
> scan request.

And this is also mentioned here (Bluetooth Core Specification v5.4, Vol 1, Part A, section 3.3.2.2.2):-

> Some advertising events sent by the advertising device permit the
> listening device (scanner) to concurrently send scan requests or
> connection requests packets on the same advertising PHY channel in
> which the advertising packet was received. The advertising device can
> send a scan response packet again on the same advertising PHY channel
> within the same advertising event.

Scan responses are usually set before a device even starts advertising, however, it is permitted that the scan response is set or changed when the device has already started advertising. This is mentioned in the Bluetooth Specification v5.4, Vol 4, Part E, 7.8.8 LES Set Scan Response Data command:-

> If advertising is currently enabled, the Controller shall use the new
> data in subsequent advertising events. If an advertising event is in
> progress when this command is issued, the Controller may use the old
> or new data for that event. If advertising is currently disabled, the
> data shall be kept by the Controller and used once advertising is
> enabled.
>
> The default Scan_Response_Data_Length shall be zero and the default
> Scan_Response_Data shall be 31 zero octets.

As for the practical part, you can easily send scan reports using the btmgmt tool and the following command on Linux:-

sudo btmgmt add-adv -d 02010606084142434400 -s 080954657374204C45

Where the -d option is for setting the advert data, and the -s is for setting the scan response data. The full list of btmgmt add-adv options are:-

Usage: add-adv [options] &lt;instance_id&gt;

Options:
	 -u, --uuid &lt;uuid&gt;         Service UUID
	 -d, --adv-data &lt;data&gt;     Advertising Data bytes
	 -s, --scan-rsp &lt;data&gt;     Scan Response Data bytes
	 -t, --timeout &lt;timeout&gt;   Timeout in seconds
	 -D, --duration &lt;duration&gt; Duration in seconds
	 -P, --phy &lt;phy&gt;           Phy type, Specify 1M/2M/CODED
	 -c, --connectable         &quot;connectable&quot; flag
	 -g, --general-discov      &quot;general-discoverable&quot; flag
	 -l, --limited-discov      &quot;limited-discoverable&quot; flag
	 -n, --scan-rsp-local-name &quot;local-name&quot; flag
	 -a, --scan-rsp-appearance &quot;appearance&quot; flag
	 -m, --managed-flags       &quot;managed-flags&quot; flag
	 -p, --tx-power            &quot;tx-power&quot; flag
e.g.:
	add-adv -u 180d -u 180f -d 080954657374204C45 1

BLE data in adverts is decoded as follows (based on the Assigned Numbers Document):-

  • 1st byte = length (n bytes)
  • 2nd byte = Types
  • n-1 bytes = actual data

So the meaning of the advert data I added:-

  • 02 Length (2 bytes)

  • 01 Type (Flags)

  • 06 Flag - 02 && 04 LE General Discoverable && BR/EDR Not supported

  • 06 Length (6 bytes)

  • 08 Type (Shortened Local Name)

  • 4142434400 (ABCD)

And the meaning of the scan response data is:-

  • 08 Length (8 bytes)
  • 09 Type (Complete Local Name)
  • 54657374204C45 (Test LE)

Some more useful links:-

答案2

得分: 1

以下是您提供的代码的翻译:

如你所说,你会接受使用任何编程语言/库的答案 - 我想指向Qt框架。特别是Qt连接模块和其蓝牙组件。要求的是一个bluez实现:在Linux上Qt蓝牙模块是围绕bluez的C++包装器。以下是一个示例摘录,其中配置了广告数据和扫描响应数据。

QLowEnergyAdvertisingData advertisingData;
QLowEnergyAdvertisingData scanResponseData; // <- 添加到原始示例中
advertisingData.setDiscoverability(QLowEnergyAdvertisingData::DiscoverabilityGeneral);
advertisingData.setIncludePowerLevel(true);
advertisingData.setLocalName("HeartRateServer");
advertisingData.setServices(QList<QBluetoothUuid>() << QBluetoothUuid::ServiceClassUuid::HeartRate);
scanResponseData.setManufacturerData(0x1234, QByteArray("Hello"));  // <- 添加到示例中
bool errorOccurred = false;
const QScopedPointer<QLowEnergyController> leController(QLowEnergyController::createPeripheral());
auto errorHandler = [&leController,&errorOccurred](QLowEnergyController::Error errorCode)
{
        qWarning().noquote().nospace() << errorCode << " 发生错误: "
            << leController->errorString();
        if (errorCode != QLowEnergyController::RemoteHostClosedError) {
            qWarning("心率服务器因错误而退出。");
            errorOccurred = true;
            QCoreApplication::quit();
        }
};
QObject::connect(leController.data(), &QLowEnergyController::errorOccurred, errorHandler);

QScopedPointer<QLowEnergyService> service(leController->addService(serviceData));
leController->startAdvertising(QLowEnergyAdvertisingParameters(), advertisingData,
                               scanResponseData);  // <- 从原始示例中修改

以上示例展示了蓝牙堆栈通常如何实现扫描响应(特别是请注意示例中的第二行和最后一行)。示例的完整源代码在 https://code.qt.io/cgit/qt/qtconnectivity.git/tree/examples/bluetooth/heartrate-server/main.cpp?h=6.4 可以在BSD许可下获取(但请注意,上述摘录已经进行了修改以突出显示如何设置扫描响应)。

我在树莓派上验证了上述代码,并且可以在NRFConnect中看到生成的BTLE广告具有添加的制造商数据。要在树莓派上复制我的结果:

  1. sudo apt install qt5-default qtcreator qtconnectivity5-examples qtconnectivity5-dev
  2. 将示例复制到可以进行工作的位置(例如 mkdir test && cd test && cp -R /usr/lib/arm-linux-gnueabihf/qt5/examples .)[我使用了我周围放着的旧树莓派 - 你的可能是更新的,因此示例将默认存在于不同的目录中]
  3. 编辑示例代码(例如 cd examples/bluetooth/heartrate-server && vim main.cpp) - 进行我上面指示的3个编辑
  4. 运行 qmake heartrate-server.pro
  5. 运行 make
  6. 运行 sudo ./heartrate-server

此示例本身并不实现信标。然而,BLE设备的广告部分在设备是信标(仅广告)还是具有服务(或服务)时基本上是相同的。连接性和响应扫描请求的能力是BLE设备的正交特性。如果你从未连接到示例中的玩具服务,那么它在功能上就是一个信标。实际上,商业信标确实实现了一个服务 - 用于信标的所有者在更新信标要传达给路人的信息时使用。

无论如何,上述方法将在实现严格作为信标的设备时使用。此外,你已经指出你想要实现 '一个信标',但没有提供关于是什么类型的信标的更多信息。通常,人们会实现iBeacon或EddyStone信标 - 当然,BLE规范足够开放,允许你发明自己的信标。

回答你关于信标实现行为的问题:扫描响应数据包的结构与广告数据包相同,通常在配置广告数据时同时配置它。例如,诺迪克堆栈就是这样工作的。显然,你可以在上面看到QT也是这样做的。

Linux原始的HCI接口稍微不同,因为必须将扫描响应指定为单独的命令,但有效负载的格式与配置广告数据相同。在启用广告过程中,同时设置广告数据和扫描响应数据。

一般来说,你的蓝牙包应该使你能够以类似设置广告数据的方式来设置扫描响应数据 - 这两者非常密切相关。我对诺迪克(softdevice)实现更熟悉,而在该特定平台上,你可以选择应对SCAN_REQ事件。话虽如此,通常的做法是预先设置扫描响应数据。从Linux用户空间来看,我怀疑你不能做其他事情:BTLE实现需要在接收到SCAN_REQ后很快发送扫描响应,所以没有时间进行往返到用户空间。这些数据需要已经在内核侧的缓冲区中。

在你的项目中,你需要做两件事:首先,指示你的设备可以响应扫描请求。在qt api中,可以使用QLowEnergyAdvertisingParameters来实现。其次,你需要提供实际的扫描响应数据(见上文)。

假设你想扩展你的Rust包以提供缺失的扫描响应功能。可以通过模拟Qt实现来实现。如果你深入研究Qt实

英文:

As you have said that you will accept answers using any programming language/library -- I'd like to point you to the Qt framework. In particular the Qt connectivity module and its bluetooth component. The ask is for a bluez implementation: on Linux, the Qt bluetooth module is a C++ wrapper around bluez. Here's an example excerpt in which both advertising data and scan response data are configured.

QLowEnergyAdvertisingData advertisingData;
QLowEnergyAdvertisingData scanResponseData; // &lt;- added to original example
advertisingData.setDiscoverability(QLowEnergyAdvertisingData::DiscoverabilityGeneral);
advertisingData.setIncludePowerLevel(true);
advertisingData.setLocalName(&quot;HeartRateServer&quot;);
advertisingData.setServices(QList&lt;QBluetoothUuid&gt;() &lt;&lt; QBluetoothUuid::ServiceClassUuid::HeartRate);
scanResponseData.setManufacturerData(0x1234, QByteArray(&quot;Hello&quot;));  // &lt;- added to example
bool errorOccurred = false;
const QScopedPointer&lt;QLowEnergyController&gt; leController(QLowEnergyController::createPeripheral());
auto errorHandler = [&amp;leController,&amp;errorOccurred](QLowEnergyController::Error errorCode)
{
        qWarning().noquote().nospace() &lt;&lt; errorCode &lt;&lt; &quot; occurred: &quot;
            &lt;&lt; leController-&gt;errorString();
        if (errorCode != QLowEnergyController::RemoteHostClosedError) {
            qWarning(&quot;Heartrate-server quitting due to the error.&quot;);
            errorOccurred = true;
            QCoreApplication::quit();
        }
};
QObject::connect(leController.data(), &amp;QLowEnergyController::errorOccurred, errorHandler);

QScopedPointer&lt;QLowEnergyService&gt; service(leController-&gt;addService(serviceData));
leController-&gt;startAdvertising(QLowEnergyAdvertisingParameters(), advertisingData,
                               scanResponseData);  // &lt;- modified from original example

The above example illustrates how bluetooth stacks generally implement the scan response (specifically, note the second line and the last line in the example). Full source for the example is at https://code.qt.io/cgit/qt/qtconnectivity.git/tree/examples/bluetooth/heartrate-server/main.cpp?h=6.4 and is available under BSD license (note, however, the above excerpt was modified to highlight how to set the scan response).

I've verified the above code on RPi, and I can see that the resulting BTLE advertisement has the added mfr data using NRFConnect. To replicate my results on rpi:

  1. sudo apt install qt5-default qtcreator qtconnectivity5-examples qtconnectivity5-dev
  2. copy the examples to somewhere you can work on them (e.g. mkdir test && cd test && cp -R /usr/lib/arm-linux-gnueabihf/qt5/examples .) [I used an old rpi I had laying around -- yours may be newer and thus the examples will live in a different directory by default]
  3. edit the example code (e.g. cd examples/bluetooth/heartrate-server && vim main.cpp) -- make the 3 edits I indicate above
  4. qmake heartrate-server.pro
  5. make
  6. sudo ./heartrate-server

This example does not implement a beacon per se. However, the advertising portion of a ble device is largely the same whether the device is a beacon (advertising only) or if it has a service (or services). Connectability and the ability to respond to a scan request are orthogonal traits for a ble device. If you never connect to the toy service in the example, then it is functionally a beacon. In practice, commercial beacons do implement a service -- for the owner of the beacon to use when updating the info that the beacon is meant to convey to passers-by.
In any case, the above method would be employed when implementing a device that is strictly a beacon. Furthermore, you've indicated that you want to implement 'a beacon' but have given no further info to go on as to what type of beacon. Typically, one implements an iBeacon or an EddyStone beacon -- of course the BLE spec is open enough to allow you to invent your own beacon, too.

To answer your question about how the beacon implementation should behave: The scan response packet has the same structure as the advertisement packet, and it is normally configured at the same time that the advertisement data is configured. The Nordic stack, for example, works this way. Obviously, you can see above that's also how QT does it.

The raw linux HCI interface does it only slightly differently in that one must specify the scan response as a separate command, but the payload has the same format as the advertising data setup. Both the adv data and the scan response data are set up during the process of enabling advertising.

In general, your bluetooth package should enable you to set the scan response in a similar way to how you set the advertising data -- the two are quite closely linked. I'm much more familiar with the Nordic (softdevice) implementation, and on that particular platform one does have the option of fielding the SCAN_REQ event. That being said, the normal way to do things is to set up the scan response data ahead of time. From Linux userland, I doubt you can do anything else: the BTLE implementation needs to send the scan response pretty quickly after receiving the SCAN_REQ so there's no time for a round trip to userland. That data needs to be already in a buffer on the kernel side.

In your project, you need to do 2 things: First, indicate that your device can respond to the scan request. In the qt api, one does this with QLowEnergyAdvertisingParameters. Second, you need to provide the actual scan resonse data (see above).

Let's say you want to extend your rust package to provide the missing scan response function. One could do so by emulating the Qt implementation. If you dig around in the Qt implementation, you can (eventually) have a look at the implementation of QLeAdvertiserBluez; the method of interest is setScanResponseData which, in turn, delegates to setData. The call to setData is made with isScanResponsedata set to true, resulting in the OcfLeSetScanResponseData command being sent to the linux hci (along with the data in the object passed as the scan response). https://code.qt.io/cgit/qt/qtconnectivity.git/tree/src/bluetooth/qleadvertiser_bluez.cpp?h=5.15 line 339. OcfLeSetScanResponseData turns out to be opcode command field 0x9, which is passed to Qt's HciManager, and thence (via socket) to the linux HCI driver.

ocf 0x9 corresponds to

#define HCI_OP_LE_SET_SCAN_RSP_DATA	0x2009
struct hci_cp_le_set_scan_rsp_data {
	__u8	length;
	__u8	data[HCI_MAX_AD_LENGTH];

https://github.com/torvalds/linux/blob/master/include/net/bluetooth/hci.h Line 1651

So -- this is all doable. Qt obviously does it. What I can't figure out is why your Rust package did not expose this rather fundamental capability. And the BlueZ 'documentation' remains an impenetrable morass. Every time I think I want to use the userspace BlueZ stuff, I give up and just use the structure definitions from hci.h

[Edit]
While following up some dangling leads from the above research I found an alternate C API that may also be of interest. This one is direct from the bluez folks, but has a couple of major downsides. https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/src/shared That's bluez.git:src/shared -- apparently it is an unofficial API with no stability guarantees. Also, documentation for it appears to be thin to the point of non-existence (no worries, though -- code is self documenting, right?). There is an example at https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/tools/eddystone.c (bluez.git:tools/eddystone.c) which could probably be altered to make a beacon with a scan response. Look at adv_tx_power_callback in eddystone.c -- one could construct a bt_hci_cmd_le_set_scan_rsp_data struct, build the data array to contain a valid manufacturer info block (or a local name, or any other valid adv block), and then send the new struct with bt_hci_send(..., BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,...)

[Final Edit]

Well, I've gotten pretty wrapped around this thing. But, for the sake of completeness: here is a full, working example of a pure (not connectable) eddystone beacon that also sends a response to the scan request. This, obviously is derived from the Qt example. It can be dropped into the above referenced heartrate server example, just replace main.cpp with this code.

In this example, I explicitly set the advertising parameters to indicate that the beacon is not connectable, but can respond to the scan request.

/***************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** &quot;Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of The Qt Company Ltd nor the names of its
**     contributors may be used to endorse or promote products derived
**     from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** &quot;AS IS&quot; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.&quot;
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include &lt;QtBluetooth/qlowenergyadvertisingdata.h&gt;
#include &lt;QtBluetooth/qlowenergyadvertisingparameters.h&gt;
#include &lt;QtBluetooth/qlowenergycharacteristic.h&gt;
#include &lt;QtBluetooth/qlowenergycharacteristicdata.h&gt;
#include &lt;QtBluetooth/qlowenergydescriptordata.h&gt;
#include &lt;QtBluetooth/qlowenergycontroller.h&gt;
#include &lt;QtBluetooth/qlowenergyservice.h&gt;
#include &lt;QtBluetooth/qlowenergyservicedata.h&gt;
#include &lt;QtCore/qbytearray.h&gt;
#include &lt;QtCore/qcoreapplication.h&gt;
#include &lt;QtCore/qlist.h&gt;
#include &lt;QtCore/qloggingcategory.h&gt;
#include &lt;QtCore/qscopedpointer.h&gt;
#include &lt;QtCore/qtimer.h&gt;

int main(int argc, char *argv[])
{
    //QLoggingCategory::setFilterRules(QStringLiteral(&quot;qt.bluetooth* = true&quot;));
    QCoreApplication app(argc, argv);

    //! [Advertising Data]
    QLowEnergyAdvertisingData advertisingData;
    QLowEnergyAdvertisingData scanRespData;

    uint8_t es2[21];  // eddystone advertising block -- taken from bluez eddystone example
    es2[0] = 0x02;   /* Field length */
    es2[1] = 0x01;   /* Flags */
    es2[2] = 0x04;   /* BR/EDR Not Supported */

    es2[3] = 0x03;   /* Field length */
    es2[4] = 0x03;   /* 16-bit Service UUID list */
    es2[5] = 0xaa;   /* Eddystone UUID */
    es2[6] = 0xfe;

    es2[7] = 0x0c;   /* Field length */
    es2[8] = 0x16;   /* 16-bit Service UUID data */
    es2[9] = 0xaa;   /* Eddystone UUID */
    es2[10] = 0xfe;
    es2[11] = 0x10;    /* Eddystone-URL frame type */
    es2[12] = 0x00;    /* Calibrated Tx power at 0m */
    es2[13] = 0x00;    /* URL Scheme Prefix http://www. */
    es2[14] = &#39;b&#39;;
    es2[15] = &#39;l&#39;;
    es2[16] = &#39;u&#39;;
    es2[17] = &#39;e&#39;;
    es2[18] = &#39;z&#39;;
    es2[19] = 0x01;    /* .org/ */

    es2[20] = 0x00;    /* Field terminator */
    advertisingData.setRawData(QByteArray((const char*)es2, 21));

    scanRespData.setManufacturerData(0x1234, QByteArray(&quot;hello&quot;));
    //! [Advertising Data]

    //! [Start Advertising]
    const QScopedPointer&lt;QLowEnergyController&gt; leController(QLowEnergyController::createPeripheral());
    //const QScopedPointer&lt;QLowEnergyService&gt; service(leController-&gt;addService(serviceData));
    auto advParams = QLowEnergyAdvertisingParameters();
    advParams.setMode(QLowEnergyAdvertisingParameters::AdvScanInd);
    leController-&gt;startAdvertising(advParams, advertisingData,
                                   scanRespData /*advertisingData*/);
    //! [Start Advertising]


    auto reconnect = [&amp;leController, advertisingData, scanRespData, advParams]() {
        leController-&gt;startAdvertising(advParams, advertisingData,
                                       scanRespData);
    };
    QObject::connect(leController.data(), &amp;QLowEnergyController::disconnected, reconnect);

    return app.exec();
}

huangapple
  • 本文由 发表于 2023年2月6日 21:38:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/75362025.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定