ESP32 BLE学习(0) — 基础架构
前言
(1)学习本文之前,需要先了解一下蓝牙的基本概念:BLE学习笔记(0.0) —— 基础概念(0)
(2) 学习一款芯片的蓝牙肯定需要先简单了解一下该芯片的体系结构,因此本文将会简单的介绍ESP32的蓝牙结构。
(3)因为乐鑫目前主推的是BLE低功耗蓝牙技术,因此我本人也主要侧重讲解BLE部分。
ESP32蓝牙系统介绍
蓝牙堆栈
(1)
ESP-IDF目前支持两个主机堆栈,Bluedroid(默认) 和Apache NimBLE。
- Bluedroid : 该堆栈支持传统蓝牙(
BR/EDR)和低功耗蓝牙(BLE)。如果是传统蓝牙(BR/EDR)有需求,则必须使用该堆栈。- Apache NimBLE : 仅支持低功耗蓝牙。如果仅仅是对
BLE有使用需求,建议选择该协议栈,因为该协议栈代码占用和运行时对内存的需求都会低一些。
蓝牙架构
(1)我们知道,蓝牙从整体架构上可以分为控制器 (
Controller) 和主机 (Host) 。
- 控制器 (
Controller) : 通常是一个物理设备,它能够发送和接收无线电信号,并懂得如何将这些信号翻译成携带信息的数据包。主要用于硬件接口管理、链路管理等等。- 主机 (
Host) : 它通常是一个软件协议栈,用于管理两台或多态设备间如何通讯以及如何实现无线电同时提供几种不同服务。它可以构建各种规范,向上层应用提供接口基础,方便应用层对蓝牙系统的访问编程。(2)首先,我们需要知道为什么蓝牙需要分为控制器 (
Controller) 和主机 (Host) 两层结构。这个时候我们就需要了解一下蓝牙技术设计初衷了,蓝牙致力于打造一种低成本的无线通讯方案,要实现低成本那么就需要有较大的销量。如今,手机作为当之无愧销量最大的消费电子设备,任何一项技术一旦进入了手机就非常容易取得成功。因此,低功耗蓝牙将会依附于蓝牙在手机上的高配售率快速拓宽市场。
(3)既然你要依附手机,那么很多东西都要从手机厂的角度进行思考问题。那么,如果你仔细阅读蓝牙核心规格,你会发现规格书更多地是站在手机角度来阐述的,然后“顺带”描述一下手机周边蓝牙设备的实现原理。
(4)大家都知道,手机厂一般不只是做手机,还会做一些手机相关的周边设备,例如华为不仅仅做华为手机,还有华为平板,华为耳机,华为电脑等等。华为手机和华为电脑只要通过无线网络连接在一起,就能够无感控制对方并且传输数据。但是,如果华为手机和苹果电脑组合在一起,却做不到无感控制对方。这是为什么呢?
(5)因为,手机厂都会在自家设备上跑一套协议栈用于适配自家的电子产品。协议栈设计的越好,手机厂自家的各种设备间信息传输更稳定,安全,用户体验也越好。因此手机厂不会把自家的协议栈分享给别人一起使用,这也导致的华为手机和苹果电脑之间部分功能无法实现的原因。(注意,虽然不同手机厂协议栈不一样,但还是符合SIG标准和规范的,因此大部分功能能够互通)
(6)既然手机厂的协议栈是不进行公开的,那么就存在一个问题,如果手机厂好不容易将自家的协议栈部署在一颗蓝牙芯片上,突然发现又有一颗性能更好、更便宜的蓝牙芯片了,需要更换芯片怎么办?这个时候需要更换部署,对于手机厂来说成本太高。因此SIG将跑协议栈的Host层与硬件管理的Control层进行隔离,中间统一一个接口标准HCI层。这样的话,手机厂只需要在AP芯片上跑协议栈Host层,而负责硬件的Control层单独一个芯片。当需要更换蓝牙芯片的时候,直接换即可,因为都是统一的HCI接口。

(7)现在我们有了上述基础,再来看看
ESP32的蓝牙结构。ESP32有一个Control层,负责物理层相关的处理。Host层就有三种应用场景:
- 单芯片跑蓝牙程序:使用
Control层和Host层都运行在ESP32上,Host层有三种选择,乐鑫官方提供的Bluedroid(默认) 和NimBLE协议栈,或者自己在ESP32上编写一个自己的协议栈。- 双芯片跑蓝牙程序:在
ESP32上运行Control层,外接一个运行蓝牙协议栈的Host层。- 认证测试:我们如果想要使用
ESP32作为开发用的蓝牙芯片,那么就需要知道它是否符合一些认证标准。因此就可以使用一个UART外接PC机进行认证测试。

代码分析
(1)我们首先需要找到gatt_server_service_table例程根据上面所说的知识分析一下
app_main()函数做了什么。
(2)该例程虽然是使用的Bluedroid协议栈,但是只用了BLE部分。
<1>esp_bt_controller_mem_release()因为我们这里只需要BLE部分,所以需要先将传统蓝牙的内存进行释放。
// 释放经典蓝牙控制器内存
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));
<2>
esp_bt_controller_init()是对Control层进行初始化,BT_CONTROLLER_INIT_CONFIG_DEFAULT()是一个宏作为蓝牙初始化的默认参数,这个宏默认仅初始化BLE部分。
// 初始化蓝牙 Control 层
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
ret = esp_bt_controller_init(&bt_cfg);
if (ret) {ESP_LOGE(GATTS_TABLE_TAG, "%s enable controller failed: %s", __func__, esp_err_to_name(ret));return;
}
<3>我们对蓝牙
Control初始化完成之后,需要调用esp_bt_controller_enable()对Control层进行使能。传入值是一个enum参数。
ESP_BT_MODE_IDLE: 失能蓝牙ESP_BT_MODE_BLE: 仅运行低功耗蓝牙(BLE)ESP_BT_MODE_CLASSIC_BT: 仅运行传统蓝牙(BR/EDR)ESP_BT_MODE_IDLE: 即运行低功耗蓝牙(BLE)又运行传统蓝牙(BR/EDR)
// 使能蓝牙 Control 层
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
if (ret) {ESP_LOGE(GATTS_TABLE_TAG, "%s enable controller failed: %s", __func__, esp_err_to_name(ret));return;
}
<4>如果我们的
Host层不是运行在ESP32上,那么只需要进行如上操作即可。但是该例程是采用的单芯片方案,因此还需要对Host层进行初始化,这是使用的Bluedroid协议栈。
// 初始化蓝牙 HOST 层
ret = esp_bluedroid_init();
if (ret) {ESP_LOGE(GATTS_TABLE_TAG, "%s init bluetooth failed: %s", __func__, esp_err_to_name(ret));return;
}
<5>初始化
bluedroid之后,再进行使能即可。
// 使能蓝牙 HOST 层
ret = esp_bluedroid_enable();
if (ret) {ESP_LOGE(GATTS_TABLE_TAG, "%s enable bluetooth failed: %s", __func__, esp_err_to_name(ret));return;
}
<6>如下部分在后面章节会进一步讲解,各位看一下代码注释简单了解即可。
// 注册 GATT 回调函数,处理所有的 GATT 事件ret = esp_ble_gatts_register_callback(gatts_event_handler);if (ret){ESP_LOGE(GATTS_TABLE_TAG, "gatts register error, error code = %x", ret);return;}// 注册 GAP 回调函数,ret = esp_ble_gap_register_callback(gap_event_handler);if (ret){ESP_LOGE(GATTS_TABLE_TAG, "gap register error, error code = %x", ret);return;}/* 注册一个app_id, 协议栈将会分配一个对应的 gatts_if,用于标识一个 GATT 服务。* 调用这个函数就会触发 esp_ble_gatts_register_callback() 注册的回调函数中的 ESP_GATTS_REG_EVT 事件*/ret = esp_ble_gatts_app_register(heart_rate_profile_tab[PROFILE_APP_IDX].app_id);if (ret){ESP_LOGE(GATTS_TABLE_TAG, "gatts app register error, error code = %x", ret);return;}// 设置本地 MTU 大小esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(500);if (local_mtu_ret){ESP_LOGE(GATTS_TABLE_TAG, "set local MTU failed, error code = %x", local_mtu_ret);}
参考
(1)乐鑫官方文档:ESP32蓝牙架构
(2)《低功耗蓝牙开发权威指南》第三章 —— 低功耗蓝牙的体系结构。
(3)《低功耗蓝牙开发权威指南》2.9章节 —— 十亿只是小目标。
(4)博客园:三种蓝牙架构实现方案(蓝牙协议栈方案)
相关文章:
ESP32 BLE学习(0) — 基础架构
前言 (1)学习本文之前,需要先了解一下蓝牙的基本概念:BLE学习笔记(0.0) —— 基础概念(0) (2) 学习一款芯片的蓝牙肯定需要先简单了解一下该芯片的体系结构&a…...
【JAVA】Java中Spring Boot如何设置全局的BusinessException
文章目录 前言一、函数解释二、代码实现三、总结 前言 在Java应用开发中,我们常常需要读取配置文件。Spring Boot提供了一种方便的方式来读取配置。在本文中,我们将探讨如何在Spring Boot中使用Value和ConfigurationProperties注解来读取配置。 一、函数…...
pdf.js实现web h5预览pdf文件(兼容低版本浏览器)
注意 使用的是pdf.js 版本为 v2.16.105。因为新版本 兼容性不太好,部分手机预览不了,所以采用v2版本。 相关依赖 "canvas": "^2.11.2", "pdfjs-dist": "^2.16.105", "core-js-pure": "^3.37.…...
SSID简介
一、 SSID 概念定义 SSID(Service Set Identifier)即服务集标识符。它是无线网络中的一个重要标识,用于区分不同的无线网络。 相当于无线网络的名称,用于区分不同的无线网络。用户在众多可用网络中识别和选择特定网络的依据。通…...
PS通过GTX实现SFP网络通信1
将 PS ENET1 的 GMII 接口和 MDIO 接口 通过 EMIO 方 式引出。在 PL 端将引出的 GMII 接口和 MDIO 接口与 IP 核 1G/2.5G Ethernet PCS/PMA or SGMII 连接, 1G/2.5G Ethernet PCS/PMA or SGMII 通过高速串行收发器 GTX 与 MIZ7035/7100 开发…...
前端面试项目细节重难点(已工作|做分享)(九)
面试官:请你讲讲你在工作中如何开发一个新需求,你的整个开发过程是什么样的? 答:仔细想想,我开发新需求的过程如下: (1)第一步:理解需求文档: 首先&#x…...
区间预测 | Matlab实现BP-ABKDE的BP神经网络自适应带宽核密度估计多变量回归区间预测
区间预测 | Matlab实现BP-ABKDE的BP神经网络自适应带宽核密度估计多变量回归区间预测 目录 区间预测 | Matlab实现BP-ABKDE的BP神经网络自适应带宽核密度估计多变量回归区间预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现BP-ABKDE的BP神经网络自适应带…...
抢占人工智能行业红利,前阿里巴巴产品专家带你15天入门AI产品经理
前言 当互联网行业巨头纷纷布局人工智能,国家将人工智能上升为国家战略,藤校核心课程涉足人工智能…人工智能领域蕴含着巨大潜力,早已成为业内共识。 面对极大的行业空缺,不少人都希望能抢占行业红利期,进入AI领域。…...
MEMS:Lecture 16 Gyros
陀螺仪原理 A classic spinning gyroscope measures the rotation rate by utilizing the conservation of angular momentum. 经典旋转陀螺仪通过利用角动量守恒来测量旋转速率。 Coriolis Effect and Coriolis Force 科里奥利效应是一种出现在旋转参考系中的现象。它描述了…...
Java中List流式转换为Map的终极指南
哈喽,大家好,我是木头左! 在Java编程中,经常需要将一个List对象转换为另一个Map对象。这可能是因为需要根据List中的元素的某些属性来创建一个新的键值对集合。在本文中,我将向您展示如何使用Java 中的流式API轻松地实…...
【秋招突围】2024届秋招笔试-小红书笔试题-第一套-三语言题解(Java/Cpp/Python)
🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系计划跟新各公司春秋招的笔试题 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 📧 清隆这边…...
HAL库开发--STM32的HAL环境搭建
知不足而奋进 望远山而前行 目录 文章目录 前言 下载 安装 解压 安装 添加开发包 修改仓库路径 下载软件开发包(慢,不推荐) 解压已有软件开发包(快,推荐) 总结 前言 在嵌入式系统开发中&#x…...
【DPDK学习路径】七、创建RX/TX队列
上一节我们讲述了如何申请内存池缓冲区以便接下来创建 RX 队列,这一节我们将给出具体如何创建 RX/TX 队列。 在 DPDK 中提供了 rte_eth_rx_queue_setup 及 rte_eth_tx_queue_setup 这两个接口用于接收/发送队列的创建。 下面给出一个为各个网卡创建RX/TX 队列的实例…...
【ArcGISProSDK】OpenItemDialog打开文件对话框
打开单个文件 效果 代码 public async void OpenFunction() {// 获取默认数据库var gdbPath Project.Current.DefaultGeodatabasePath;OpenItemDialog openItemDialog new OpenItemDialog() { Title "打开要素文件",InitialLocation gdbPath,Filter ItemFilte…...
TensorFlow2.x基础与mnist手写数字识别示例
文章目录 Github官网文档Playground安装声明张量常量变量 张量计算张量数据类型转换张量数据维度转换ReLU 函数Softmax 函数卷积神经网络训练模型测试模型数据集保存目录显示每层网络的结果 TensorFlow 是一个开源的深度学习框架,由 Google Brain 团队开发和维护。它…...
大数据开发语言Scala入门
Scala是一种多范式编程语言,它集成了面向对象编程和函数式编程的特性。Scala运行在Java虚拟机上,并且可以与Java代码无缝交互,这使得它成为大数据处理和分析领域中非常受欢迎的语言,尤其是在使用Apache Spark这样的框架时。 Scal…...
【CDN】逆天 CDN !BootCDN 向 JS 文件中植入恶意代码
今天在调试代码,突然控制台出现了非常多报错。 这非常可疑,报错指向的域名也证实了这一点。 因为我的 HTML 中只有一个外部开源库(qrcode.min.js),因此只有可能是它出现了问题。 我翻看了请求记录,发现这…...
摆脱Jenkins - 使用google cloudbuild 部署 java service 到 compute engine VM
在之前 介绍 cloud build 的文章中 初探 Google 云原生的CICD - CloudBuild 已经介绍过, 用cloud build 去部署1个 spring boot service 到 cloud run 是很简单的, 因为部署cloud run 无非就是用gcloud 去部署1个 GAR 上的docker image 到cloud run 容…...
【CS.PL】Lua 编程之道: 控制结构 - 进度24%
3 初级阶段 —— 控制结构 文章目录 3 初级阶段 —— 控制结构3.1 条件语句:if、else、elseif3.2 循环语句:for、while、repeat-until3.2.1 输出所有的命令行参数3.2.2 while.lua3.2.3 repeat.lua及其作用域 🔥3.2.4 for.lua (For Statement)…...
从“数据孤岛”、Data Fabric(数据编织)谈逻辑数据平台
提到逻辑数据平台,其核心在于“逻辑”,与之相对的便是“物理”。在过去,为了更好地利用和管理数据,我们通常会选择搭建数据仓库和数据湖,将所有数据物理集中起来。但随着数据量、用数需求和用数人员的持续激增…...
深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
智能AI电话机器人系统的识别能力现状与发展水平
一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...
