蓝牙4.2:ESP32的收发流程代码

基于上篇文章,大致了解的ESP32蓝牙收发的流程,我们知道接收端的代码不需要更改,接收端只需要开启扫描,拿到空中的来自于发送端的蓝牙广播数据包即可,那么我们对发送端的代码进行更改。

在发送前,我们需要填充广播包的数据,这需要调用乐鑫官网的API,而这个填充过程大致分为以下四步

这就是我们要做的事情,接下来只需要跟着这个逻辑去调用乐鑫官网的API就好了。

从直译来看,这就是配置广播数据的API

这是开始广播的API

开始填充main函数

但是我们并没有adv_data和adv_params这两个结构体,为了先搭框架,我们从

这个文件里复制粘贴过来,因为这个工程是官方demo,写好了发送时结构体示例,我们先拿过来,后续再更改

static esp_ble_adv_data_t adv_data = {
    .set_scan_rsp = false,
    .include_name = true,
    .include_txpower = true,
    .min_interval = 0x0006, //slave connection min interval, Time = min_interval * 1.25 msec
    .max_interval = 0x000C, //slave connection max interval, Time = max_interval * 1.25 msec
    .appearance = 0x00,
    .manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN,
    .p_manufacturer_data =  NULL, //&test_manufacturer[0],
    .service_data_len = 0,
    .p_service_data = NULL,
    .service_uuid_len = 32,
    .p_service_uuid = adv_service_uuid128,
    .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};

static esp_ble_adv_params_t adv_params = {
    .adv_int_min        = 0x20,
    .adv_int_max        = 0x40,
    .adv_type           = ADV_TYPE_IND,
    .own_addr_type      = BLE_ADDR_TYPE_PUBLIC,
    //.peer_addr            =
    //.peer_addr_type       =
    .channel_map        = ADV_CHNL_ALL,
    .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
};

把拿过来的结构体复制到main函数上方进行定义,这里注意.p_service_uuid = adv_service_uuid128,这个adv_service_uuid128是没有定义的,也应该去刚才的文件里拿过来进行粘贴定义,uuid给16位就够用了,没必要用源代码里的32位,所以将结构体中.service_uuid_len=32改成16。由于用的是谷歌的数据包格式(Eddystone),所以将uuid128这个数组的12,13位改成0xAA,0xFE

static uint8_t adv_service_uuid128[16] = {
    /* LSB <--------------------------------------------------------------------------------> MSB */
    //first uuid, 16bit, [12],[13] is the value
    0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xAA, 0xFE, 0x00, 0x00,

};

然后我们对服务数据进行封包,假设封包的对象是UUID,我们写一个封装UUID的代码

/********************************************************************
函数名:		Eddystone_Set_UID

描述:			设置eddystone UID 帧格式参数

参数:			uid 	- uid指针
				power 	- 功率
				name_space 	- 命名空间
				instance	- 实例

返回值:		错误	-1
				正确	服务数据长度	
********************************************************************/
int Eddystone_Set_UID(eddystone_uid_t *uid,char power,uint8_t * name_space,uint8_t *instance)
{
	if(uid == NULL || name_space == NULL || instance == NULL)
	{
		return -1;
	}
	memset(uid,0,sizeof(eddystone_uid_t));
	uid->uuid = EDDYSTONE_SERVICE_UUID;
	uid->frame_type = EDDYSTONE_UID;
	uid->tx_power = (uint8_t)power;
	for(int i = 0;i <10;i++)
		uid->name_space[i] = *name_space++;

	for(int i = 0;i <6;i++)
		uid->instance[i] = *instance++;

	return sizeof(eddystone_uid_t);
}

这个函数设置了UID的帧格式,返回一个服务数据长度,这个长度是干嘛用的?我们再来看看刚才复制过来的结构体

实际上我们发送数据时需要填充这两个成员,分别是服务数据长度和服务数据,所以Eddystone_Set_UID需要返回一个服务数据长度以填充该结构体。Eddystone_Set_UID(eddystone_uid_t *uid,char power,uint8_t * name_space,uint8_t *instance)有四个参数,分别是eddystone_uid_t类型的uid,char类型power,u8类型的namespace命名空间和instance实例,uid定义一个eddystone_uid_t的变量即可,power是功率,用于定位,这里不需要定位功能,随便填即可,那么namespace和instance怎么去填?

我们先回过头看看UID帧格式是什么样子的。

命名空间和实例的定义:

        简单来说,命名空间用于分组,像一个大的“文件夹”,比如在一个大型商场里,可能会有多个不同区域的蓝牙信标,像服装区、餐饮区、超市区等,就可以给每个区域的信标设置不同的命名空间。这样,当我们想要对某个区域的信标进行统一管理、操作或者获取信息时,只需要找到对应的命名空间就行。实例在一个命名空间这个大 “文件夹” 里面,实例就像是一个个具体的文件,用来唯一地标识每一个具体的蓝牙设备。比如在服装区这个命名空间下,每一个具体的服装店铺门口的蓝牙信标都有自己独特的实例号,这样我们就可以准确地找到想要的那个店铺的信标,获取到这个店铺特有的信息,比如正在进行的促销活动等。

命名空间大小是十个字节,实例为六个字节。那我们给命名空间填“Helloworld”,实例为"123456"。

根据这个逻辑,我们写代码

发送方代码流程基本就这些了,编译烧写就好了,至于接收方,只需要烧写官方demo就可以了

大致流程就这些,思路理清了和把帧格式弄明白就很好写了

<think>好的,我现在需要帮助用户了解如何实现两个ESP32设备之间的蓝牙通信,特别是收发数据的示例。首先,我需要回忆一下ESP32蓝牙功能和相关文档。根据引用[1],ESP32集成了蓝牙和Wi-Fi,适合物联网应用,蓝牙串口配置文件(SPP)用于数据通信。引用[2]和[3]提到了GATT协议和收发流程代码,这可能涉及BLE(低功耗蓝牙)方面的内容。 用户的问题是关于两个ESP32之间的蓝牙通信示例,可能需要区分是经典蓝牙还是BLE。根据引用[3]中的信息蓝牙4.2收发流程可能指的是BLE,因为经典蓝牙通常使用SPP。但需要确认ESP32支持的蓝牙模式。ESP32支持经典蓝牙和BLE,所以可能需要分别说明两种情况。 首先,经典蓝牙(SPP)通常用于持续的数据传输,比如串口通信,而BLE适用于低功耗、间歇性数据传输。用户可能需要其中一个或两者。但示例可能需要选择一种,或者分别给出两种方案。根据引用[1]提到的SPP,可能用户想要的是经典蓝牙的例子。但引用[2][3]更侧重于BLE的GATT,所以可能需要明确。 接下来,我需要查阅相关代码示例。对于经典蓝牙SPP,通常一个设备作为服务器,另一个作为客户端。服务器监听连接,客户端发起连接。数据通过串行端口配置文件传输。在Arduino或ESP-IDF环境中,可能需要使用BluetoothSerial库。 对于BLE的情况,需要设置GATT服务器和客户端,定义服务和特征值,通过特征值的读写或通知实现数据传输。引用[2]中的GATT数据通信可能涉及创建自定义服务和特征,处理读写事件。 现在需要整理步骤,编写示例代码。假设用户使用Arduino框架,因为代码可能更简洁。首先处理经典蓝牙SPP的情况: 1. 服务器端代码:初始化蓝牙,开始监听,等待连接,然后发送或接收数据。 2. 客户端代码:扫描并连接到服务器,然后进行数据交换。 然后处理BLE的情况: 1. 一个设备作为GATT服务器,创建服务和特征,启用通知。 2. 另一个设备作为客户端,连接后订阅特征的通知,或写入特征值。 需要确保代码正确,语法无误,符合用户的要求。同时,根据引用[3],发送端可能需要修改广播数据,接收端扫描获取数据包。 可能还需要考虑配对和连接的安全性,但用户可能只需要基础示例。因此,分步骤说明两种方式,并给出代码示例。同时,在回答中引用相关的引用[1][2][3]来支持说明。 最后,生成相关问题,例如性能优化、数据安全、错误处理等,帮助用户进一步学习。</think>### 如何实现两个ESP32设备之间的蓝牙通信 #### 一、通信方式选择 ESP32支持经典蓝牙(SPP)和低功耗蓝牙(BLE)两种模式: 1. **经典蓝牙(SPP)**:适用于高速、持续数据传输(如文件传输或实时控制),需配对后建立稳定连接[^1]。 2. **低功耗蓝牙(BLE)**:适用于低功耗、间歇性数据传输(如传感器数据上报),基于GATT协议定义服务特征[^2]。 以下分别提供两种模式的实现示例。 --- #### 二、经典蓝牙(SPP)通信示例 ##### 1. 服务器端代码(接收数据) ```cpp #include <BluetoothSerial.h> BluetoothSerial SerialBT; void setup() { Serial.begin(115200); SerialBT.begin("ESP32_Server"); // 设置设备名称 Serial.println("等待客户端连接..."); } void loop() { if (SerialBT.available()) { String data = SerialBT.readString(); Serial.print("接收数据: "); Serial.println(data); } delay(100); } ``` ##### 2. 客户端代码(发送数据) ```cpp #include <BluetoothSerial.h> BluetoothSerial SerialBT; void setup() { Serial.begin(115200); if (!SerialBT.begin("ESP32_Client", true)) { // 客户端模式 Serial.println("初始化失败"); return; } bool connected = SerialBT.connect("ESP32_Server"); // 连接服务器 if (connected) { Serial.println("连接成功"); SerialBT.println("Hello from Client!"); } } void loop() { delay(1000); } ``` --- #### 三、低功耗蓝牙(BLE)通信示例 ##### 1. BLE服务器端(GATT服务提供者) ```cpp #include <BLEDevice.h> #include <BLEServer.h> #include <BLEUtils.h> #define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" BLEServer *pServer; BLEService *pService; BLECharacteristic *pCharacteristic; void setup() { Serial.begin(115200); BLEDevice::init("ESP32_BLE_Server"); pServer = BLEDevice::createServer(); pService = pServer->createService(SERVICE_UUID); // 创建可读写的特征 pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE ); pCharacteristic->setValue("Initial Value"); pService->start(); BLEAdvertising *pAdvertising = pServer->getAdvertising(); pAdvertising->start(); Serial.println("BLE服务器已启动"); } void loop() { delay(2000); } ``` ##### 2. BLE客户端(数据请求者) ```cpp #include <BLEDevice.h> #include <BLEUtils.h> #include <BLEScan.h> #define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" BLEClient* pClient; BLERemoteCharacteristic* pRemoteCharacteristic; void setup() { Serial.begin(115200); BLEDevice::init("ESP32_BLE_Client"); pClient = BLEDevice::createClient(); // 扫描并连接服务器 BLEScan* pScan = BLEDevice::getScan(); pScan->setActiveScan(true); BLEScanResults results = pScan->start(5); BLEAdvertisedDevice serverDevice; for (int i = 0; i < results.getCount(); i++) { BLEAdvertisedDevice device = results.getDevice(i); if (device.getName() == "ESP32_BLE_Server") { serverDevice = device; break; } } pClient->connect(&serverDevice); BLERemoteService* pRemoteService = pClient->getService(SERVICE_UUID); pRemoteCharacteristic = pRemoteService->getCharacteristic(CHARACTERISTIC_UUID); // 读取特征值 std::string value = pRemoteCharacteristic->readValue(); Serial.print("读取值: "); Serial.println(value.c_str()); } void loop() { delay(1000); } ``` --- #### 四、关键配置说明 1. **UUID设计**:需为服务和特征定义唯一UUID,避免其他设备冲突[^2]。 2. **数据格式**:通过`setValue()`和`readValue()`传递数据时,需统一编码格式(如字符串或二进制)。 3. **连接稳定性**:经典蓝牙需处理断线重连逻辑,BLE需管理连接间隔参数[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值