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(数据编织)谈逻辑数据平台
提到逻辑数据平台,其核心在于“逻辑”,与之相对的便是“物理”。在过去,为了更好地利用和管理数据,我们通常会选择搭建数据仓库和数据湖,将所有数据物理集中起来。但随着数据量、用数需求和用数人员的持续激增…...
vuex4.x 升级pinia,router 中使用同步组件导致项目启动失败
背景描述 升级的项目本来是vue2的项目,先升级成vue3,这个过程相关的问题都被决绝,当时状态管理使用的还是vuex4.x版本。 后面发现变成复杂模块时,后续再对复杂模块的功能进行迭代时,由于js的弱类型,改动时…...
0. 云原生之基于乌班图远程开发
云原生专栏大纲 文章目录 安装乌班图配置静态IP重置root密码开启root远程登录开启远程SSH访问安装docker安装docker-compose安装Edge浏览器安装搜狗输入法安装TeamViewer安装虚拟显示器安装JDK安装maven安装vscodevscode插件安装VSCode配置maven、git、jdk、自动报错vscode快捷…...
C++ 字符串处理5-手机号邮箱如何脱敏处理
1. 关键词2. strutil.h3. strutil.cpp4. 测试代码5. 运行结果6. 源码地址 1. 关键词 关键词: C 字符串处理 分割字符串 连接字符串 跨平台 应用场景: 有些重要信息需要保密,比如手机号、邮箱等,如何在不影响用户阅读的情况下…...
【lesson8】云备份服务端完整版代码
文章目录 util.hppconfig.hpphot.hppdata.hppserver.hppserver.ccMakefilecloud.conf util.hpp #pragma once #include <iostream> #include <fstream> #include <string> #include <vector> #include <sys/stat.h> #include <unistd.h> …...
AI办公自动化:kimi批量搜索提取PDF文档中特定文本内容
工作任务:PDF文档中有资料来源这一行,比如: 资料来源:moomoo tech、The Information、Bloomberg、Reuters,浙商证券研究所 数据来源:CSDN、浙商证券研究所 数据来源:CSDN、arXiv、浙商证券研…...
基于C#开发web网页管理系统模板流程-总集篇
第一篇 基于C#开发web网页管理系统模板流程-登录界面和主界面_c#的网页编程-CSDN博客 第二篇 基于C#开发web网页管理系统模板流程-主界面管理员录入和编辑功能完善_c#网页设计-CSDN博客 第三篇 基于C#开发web网页管理系统模板流程-主界面管理员入库和出库功能完善_c#web程序设计…...
什么是DMZ?路由器上如何使用DMZ?
文章目录 📖 介绍 📖🏡 演示环境 🏡📒 DMZ 📒🚀 DMZ的应用场景💡 路由器设置DMZ🎈 注意事项 🎈⚓️ 相关链接 ⚓️📖 介绍 📖 在网络管理中,DMZ(Demilitarized Zone,隔离区)是一个特殊的网络区域,常用于将公共访问和内部网络隔离开来。DMZ功能允许…...
【bugfix】解决Redis缓存键清理问题
前言 在Spring Boot应用中集成Redis作为缓存存储时,合理配置RedisTemplate是确保数据正确存储和检索的关键。本文将通过对比分析一段初始存在问题的Redis配置代码及其修正后的版本,探讨如何正确处理Redis键前缀,以避免清理缓存时遇到的问题。…...
泛微开发修炼之旅--15后端开发连接外部数据源,实现在ecology系统中查询其他异构系统数据库得示例和源码
文章链接:15后端开发连接外部数据源,实现在ecology系统中查询其他异构系统数据库得示例和源码...
弹幕逆向signature、a_bogus
声明 本文章中所有内容仅供学习交流使用,不用于其他任何目的,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关! 本文章未经许可禁止转载&a…...
网站改版 合同/沈阳百度seo排名优化软件
#正则表达式select * from employee where name like jin%;select * from employee where name regexp ^jin;select * from employee where name regexp ^jin.*(g|n)$; 转载于:https://www.cnblogs.com/FlFtFw/p/9544251.html...
从哪些方面评价一个企业的网站建设/网址域名查询ip地址
HTML的注释 <!-- 注释内容 -->而且这个注释是多行的 注释的嵌套是有问题的,所以注释是不能嵌套的 HTML简史 1993 HTML 第一版 只是一些草案,每个浏览器都可以有自己特定的标准 网景(Netscape)和微软(Micros…...
网站内容 优化/seo人员招聘
MySQL内部存储代码有四种:触发器,存储过程,函数,事件。 优点: l 在服务器内部执行,离数据最近,另外在服务器上执行还可以节省带宽和网络延迟。 l 一种代码重用。可以方便地统一业务规则&…...
凡科建站是不是免费的/交换友情链接是什么意思
一、要拍出整体清晰的泛焦效果 当整体要清楚对焦时,记着要缩小光圈,把焦距拉远(广角),并对焦在中间前一点的位置。 利用f/11光圈,把对焦点放在「对焦范围」位置,加上16mm的广角,轻易拍出全部清晰的相片。 二…...
thinkphpcmf网站开发/seo的重要性
Android Studio Profiler 检查内存简单介绍 如何使用? 第一步:点击Profiler按钮 第二步:选择 第三步:选择Capture heap dump 并点击Record 解释相关按钮的功能 垃圾桶按钮:用于强制执行垃圾回收事件的按钮ÿ…...
做电子商务系统网站/晋城今日头条新闻
这个问题严格来说是肯定有的,kafka只能保证分区内的有序性。 下面是kafka作者Jay Kreps的blog中介绍kafka设计思想的一段话。 Each partition is a totally ordered log, but there is no global ordering between partitions (other than perhaps some wall-clock…...