蓝牙ble的常见概念

news2024/12/23 12:19:18

蓝牙广播

包组成结构

低功耗蓝牙一共有40个信道,频段范围从2402Mhz-2480Mhz,每2Mhz一个信道,37 38 39 是广播信道,其余为数据信道

在这里插入图片描述

一个广播信道最长37字节,有6字节用作蓝牙设备的MAC地址,我们只需要关注剩余的31个字节就可以了,这31个字节又给分为若干个广播数据体,蓝牙规范中称为AD Structure,每个结构体又分为三部分组成,分别是长度,类型,内容,其中长度占用一个字节,类型一个字节,内容占用若干个字节,长度=类型的字节数+内容占用的字节数=1+N

在这里插入图片描述

我们来看一下一个具体的例子,下面是一个广播数据包包含两个AD structure,第一个数据包的长度是4,表示后面四个字节的第一个字节为类型,后面三个字节的为数据;第二个数据的长度为3,表示后面三个字节的第一个字节为类型,后两个字节为数据。如果不够31个字节系统会自动补全

在这里插入图片描述

我们再来看一下下面这个例子,包含三个AD structure,三个数据类型分别为设备名称,发射功率,厂商自定义数据,第一个AD structure表示这个数据设备名称为1234,第二个AD structure表示发射功率是8dBm

蓝牙广播分类

蓝牙广播类型可以分为如下四类,分别为

  • 可连接非定向(最常用的广播方式)

  • 可连接定向(用于已配对设备中的快速连接)

  • 不可连接非定向(用于蓝牙信标,传感器)

  • 可扫描非定向,(可扫描响应承载更多的数据)

在这里插入图片描述

蓝牙广播和扫描响应

在这里插入图片描述

广播数据是主动发射的,而扫描响应的数据是在收到其他设备的扫描请求之后才会触发的,蓝牙广播最多只能广播31个字节的数据,如果想发送更多的字节,我们可以把一部分数据放到扫描响应里面。

  • 扫描响应数据和广播数据格式是一样的

  • 扫描响应数据是非必须的

  • 扫描响应可作为广播数据的补充

蓝牙状态切换

蓝牙链路层的状态机一共有5种状态,分别是就绪态,广播态,扫描态,发起态,连接态

在这里插入图片描述

设备上电后就会处于就绪态,发起广播就会进入广播态,如果被别的设备连接就会进入连接态,断开连接会再次回到就绪态,蓝牙主机设备可以在就绪态发起扫描,进入扫描态。如果发现了想要连接的设备,可以发起连接,此时将进入连接态,如果对对方接受了连接,则双方都会进入连接态,这就是蓝牙的状态机,我们在编程的时候需要控制蓝牙的状态或者根据状态的改变来做出一些动作,

服务和特性

低功耗蓝牙设备之间通信,都是基于服务和特性。一个蓝牙设备中可以包含若干个服务,一个服务中可以包含若干个特性,每一个服务或者特性都要有一个UUID。蓝牙的数据交互都是基于一个个特性进行的,数据交互的方式有五种,分别是ReadWriteWrite WithOutResponsNotifyIndication

  • NOTIFY:从机可以通知主机,不检查使能通知 CCC (不需要对方回应答包,没有流控)。

  • INDICATE:从机可以指示主机,不检查使能通知 CCC (需要对方回应答包,有流控)

  • READ:主机读,有流控

  • WRITE:主机写,有流控

  • WRITE_WITHOUT_RESPOND:主机写了不回应,没有流控

主机–>从机:READ、WRITE、WRITE_WITHOUT_RESPOND。

从机–>主机:NOTIFY、INDICATE。

(1)WRITE、WRITE_WITHOUT_RESPONSE 是 CLIENT 端(GATT 主机角色)向 SERVER 端(GATT 从机角色)执行的发送数 据操作。而 NOTIFY 和 INDICATE 是 SERVER 端向 CLIENT 执行的发送数据操作。操作是以 handle 的方式标识。

(2)WRITE、INDICATE 的操作是需要对方响应回复命令,多用于数据交互带流控和可靠的传输方式。 (3)WRITE_WITHOUT_RESPONSE 、NOTIFY 是不需要对方响应回复,多用于数据快速传输的方式。

另外增加私有的特征的特性值关键字有 DYNAMIC,AUTHENTICATION_REQUIRED,分别代表意思如下: DYNAMIC —数据可变处理,当有 READ,WRITE,WRITE_WITHOUT_RESPONSE,会产生对应的回调函数 read_callback 和 write_callback 处理,执行获取长度,填入对应的数据等操作。 AUTHENTICATION_REQUIRED —需要配对加密认证标记,代表 CLIENT 端操作该特征的读写必需要经过配对加密后才能被允许,否则操作失败。SERVER 端可以使用该关键字,指示 CLIENT 端需要发起配对加密流程(SERVER 端常用的请求加密方式)。

服务和特性创建

蓝牙设备要在进入广播态之前创建服务和特性,在MicroPython中大概分为四步:

① 创建要使用的UUID;

② 使用UUID创建特性并设置特性的读写权限;

③ 将创建好的特性添加到服务集合中;

④ 将服务集合注册到协议栈中。

比如我们要创建一个UUID为9011的服务,该服务里面包含两个特性,这两个特性的UUID分别是9012和9013,9012的特性拥有Read和Write的权限,9013的特性拥有Read和Notify的权限。

UUID

蓝牙设备在应用层是通过服务和特性去实现的,用下面这张图进行表示,一个服务里面包含若干个特性,每个特性里面又可以有读写,通知等权限,每一个服务和特性都要有一个UUID,UUID是蓝牙组织定义的,用于区分各个服务和特性的标识符,总长度是128bit,比如下面就是两个标准的UUID

在这里插入图片描述

考虑到UUID太长,蓝牙组织设置看一个基地址,允许用户使用16bit的UUID与该基地址拼接形成128bit的UUID,比如16bit的UUID 2A37对应128bit的UUID是这样的,

在这里插入图片描述

数据交互

低功耗蓝牙之间的数据交互都是基于特性,以手机连接蓝牙模块为例,手机读取蓝牙模块的数据,使用的是Read方法,手机发送数据给蓝牙模块使用的是Write方法,蓝牙模块发送数据到手机,一般使用的是Notify方法,而且手机端还要打开Notify监听。通信示例代码如下:

from machine import Pin
from time import sleep_ms
import ubluetooth        #导入BLE功能模块

ble = ubluetooth.BLE()   #创建BLE设备
ble.active(True)         #打开BLE

#创建要使用的UUID
SERVER_1_UUID = ubluetooth.UUID(0x9011)
CHAR_A_UUID = ubluetooth.UUID(0x9012)
CHAR_B_UUID = ubluetooth.UUID(0x9013)

#创建特性并设置特性的读写权限
CHAR_A = (CHAR_A_UUID, ubluetooth.FLAG_READ | ubluetooth.FLAG_WRITE | ubluetooth.FLAG_NOTIFY, )
CHAR_B = (CHAR_B_UUID, ubluetooth.FLAG_READ | ubluetooth.FLAG_NOTIFY, )

SERVER_1 = (SERVER_1_UUID, (CHAR_A , CHAR_B, ) , ) #把特性A和特性B放入服务1
SERVICES = (SERVER_1, ) #把服务1放入服务集和中
((char_a, char_b), ) = ble.gatts_register_services(SERVICES) #注册服务到gatts

#设置BLE广播数据并开始广播
ble.gap_advertise(100, adv_data = b'\x02\x01\x06\x03\x09\x41\x42')

def ble_irq(event, data): # 蓝牙中断函数
    if event == 1: #蓝牙已连接
      print("BLE 连接成功")

    elif event == 2: #蓝牙断开连接
      print("BLE 断开连接")
      ble.gap_advertise(100, adv_data = b'\x02\x01\x06\x03\x09\x41\x42')

    elif event == 3: #收到数据
      onn_handle, char_handle = data #判断是来自那个特性的消息
      buffer = ble.gatts_read(char_handle) #读取接收到的消息
      print(char_handle, buffer) #打印消息内容
      ble.gatts_notify(0, char_handle, 'Hello') #回复Hello

ble.irq(ble_irq)

在上述代码中,我们创建了一个UUID为9011的服务,并在该服务中创建了一个UUID为9012的特性,该特性的操作权限是 Read,Write,Notify。在手机端打开谷雨蓝牙小程序,连接设备,选择UUID为9012的特性,进入常规试图,点击监听,随便写入一下数据,可以看到,设备回复给手机Hello。

在这里插入图片描述

低功耗蓝牙设备之间通信常用的方式是Read,Write 和 Notify ,必须在创建特性时赋予对应的权限,才能在通信中使用。如果某个特性在创建的时候,没有开启 Write 权限,则手机将无法通过该特性发送数据到设备。

已定义的16Bit UUID

对于一些常用的功能,蓝牙组织联盟已经为其定义好了UUID,我们在开发产品的时候直接使用即可。

16BitUUID定义文档下载地址:https://btprodspecificationrefs.blob.core.windows.net/assigned-values/16-bit%20UUID%20Numbers%20Document.pdf

电池电量显示

在上述文档中定义了电池电量服务的UUID是0x180F,电池电量特性的UUID是0x2A19,我们可以使用这两个UUID实现电池电量指示的功能,代码如下:

from machine import Pin
from time import sleep_ms
import ubluetooth        #导入BLE功能模块

ble = ubluetooth.BLE()   #创建BLE设备
ble.active(True)         #打开BLE

#创建电池服务和特性的UUID
BATTERY_SERVER_UUID = ubluetooth.UUID(0x180F)
BATTERY_CHAR_UUID = ubluetooth.UUID(0x2A19)

#创建特性并设置特性的读写权限
BATTERY_CHAR = (BATTERY_CHAR_UUID, ubluetooth.FLAG_READ , )

BATTERY_SERVER = (BATTERY_SERVER_UUID, (BATTERY_CHAR, ) , ) #把电量特性放入电池服务
SERVICES = (BATTERY_SERVER, ) #把电池服务服务放入服务集和中

((battery_char,), ) = ble.gatts_register_services(SERVICES) #注册服务到gatts

ble.gatts_write(battery_char, b'\x50') #设置电池电量为80%

#设置BLE广播数据并开始广播
ble.gap_advertise(100, adv_data = b'\x02\x01\x06\x03\x09\x41\x42')

def ble_irq(event, data): # 蓝牙中断函数
    if event == 1: #蓝牙已连接
      print("BLE 连接成功")

    elif event == 2: #蓝牙断开连接
      print("BLE 断开连接")
      ble.gap_advertise(100, adv_data = b'\x02\x01\x06\x03\x09\x41\x42')

    elif event == 3: #收到数据
      print("收到新消息")

ble.irq(ble_irq) 

电池电量用一个字节来表示,其单位是电量百分比,16进制数0x50转换成十进制为80,所以运行上述代码,连接该设备后,可以看到该设备的电量显示,如下图所示:

在这里插入图片描述

通过手机微信小程序“谷雨蓝牙”连接该设备后,可以看到如下服务列表:

温湿度传感器

在蓝牙组织联盟发布的16Bit UUID 中,定义了环境传感器服务的UUID是 ,温度特性的UUID是0x2A6E,湿度特性的UUID是0x2A6F。使用如下代码可实现简单温湿度传感器功能:

from machine import Pin
from time import sleep_ms
import ubluetooth        #导入BLE功能模块

ble = ubluetooth.BLE()   #创建BLE设备
ble.active(True)         #打开BLE

#创建环境传感器服务和特性的UUID
ENV_SERVER_UUID = ubluetooth.UUID(0x181A) #环境传感器服务
TEM_CHAR_UUID = ubluetooth.UUID(0x2A6E)   #温度特性
HUM_CHAR_UUID = ubluetooth.UUID(0x2A6F)   #湿度特性

#创建特性并设置特性的读写权限
TEM_CHAR = (TEM_CHAR_UUID, ubluetooth.FLAG_READ , )
HUM_CHAR = (HUM_CHAR_UUID, ubluetooth.FLAG_READ , )

ENV_SERVER = (ENV_SERVER_UUID, (TEM_CHAR, HUM_CHAR, ) , ) #把温湿度特性放入环境服务
SERVICES = (ENV_SERVER, ) #把环境服务放入服务集和中

((tem_char, hum_char, ), ) = ble.gatts_register_services(SERVICES) #注册服务到gatts

ble.gatts_write(tem_char, b'\x06\x08') #设置温度为20.54度(0x0806 = 2054)
ble.gatts_write(hum_char, b'\x09\x07') #设置湿度为18.01%(0x0709 = 1801)

#设置BLE广播数据并开始广播
ble.gap_advertise(100, adv_data = b'\x02\x01\x06\x03\x09\x41\x42')

def ble_irq(event, data): # 蓝牙中断函数
    if event == 1: #蓝牙已连接
      print("BLE 连接成功")

    elif event == 2: #蓝牙断开连接
      print("BLE 断开连接")
      ble.gap_advertise(100, adv_data = b'\x02\x01\x06\x03\x09\x41\x42')

    elif event == 3: #收到数据
      print("收到新消息")

ble.irq(ble_irq) 

运行上述代码,使用手机APP nRF Connect 连接设备后,可以读取到温湿度数据。温湿度数据使用两个字节来表示(低字节在前,高字节在后),温度的单位是0.01度,湿度的单位是0.01%

在这里插入图片描述

蓝牙组织联盟定义了很多常用的UUID,上面的示例只选取了其中的两个用作演示,有兴趣的同学可以自行尝试下别的UUID。

GATT

GATT全称为Generic Attribute Profile,是蓝牙(Bluetooth)4.0及以上版本中用于设备间通讯的协议。GATT定义了蓝牙设备之间如何传输数据,以及如何解释和使用这些数据。

在GATT协议中,蓝牙设备被视为包含一组服务(Service)和特征(Characteristic)的集合体。服务表示一个或多个相关的特征,而特征则包括属性值(Value)、类型(Type)和描述符(Descriptor)。通过GATT协议,蓝牙设备可以读取和写入对应特征的属性值,并针对特定的应用需求进行相应配置。

例如,在一个智能手环和智能手机之间的连接过程中,通过GATT协议,智能手环向智能手机提供了一组服务,这些服务包括手环实时步数监控等功能,并提供相应的特征及其属性值。当用户在智能手机的应用程序中选择查看手环步数时,智能手机会向手环发送请求,通过GATT协议来读取相应的特征属性值,从而数据交互得以完成。

总之,GATT协议为蓝牙设备间的通信提供了标准的规范和约束,使不同类型的蓝牙设备能够相互通信和交换数据。

profile

在BLE协议中,Profile是指一种特定的功能规范,定义了一组服务和特征,以便不同厂家的设备之间可以互相交流和理解数据。简单来说,Profile就是基于BLE通信的应用场景和特殊需求,为系统提供标准化的API接口。

Profile在BLE协议栈中位于应用层之上,在BLE协议的属性协议(ATT)和通用属性协议(GATT)之外。一个Profile通常由一个或多个服务组成,每个服务包含多个特征和描述符。具体而言,服务下面可以包括多个特征,而特征则对应了一个 UUID、一个属性值、一个权限等参数。

比如,心率测量Profile规定了在使用BLE连接心率传感器时需要实现的服务和特征,包括读取心率数值、测量开始和停止等操作。只要设备符合对应Profile的规范,就能通过这些服务和特征实现设备间的无缝交互和控制。

总之,BLE Profile提供了基于BLE通信的特定功能规范,使开发者可以更加方便地为自己的产品进行开发和设计,从而实现设备的互动和控制。

GATT Services profile结构如下图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HRQSTt0P-1686832458175)(null)]

GATT和profile有什么关系

Profile和GATT(Generic Attribute Profile)是紧密相关的概念,可以说Profile是建立在GATT之上的应用场景。GATT是BLE协议栈中定义的一个规范,它定义了设备间传输数据所用到的各种协议和格式。

在GATT中,所有设备都被分为服务器(Server)和客户端(Client)两种角色。服务器包含一项或多项服务(Service),而每个服务下则包含一项或多项特征(Characteristic)。每个特征都具有固定的UUID、属性值、权限等属性,并提供一个句柄(Handle)作为标识。

而Profile可以理解为一组特定的服务和特征的集合,通常由Bluetooth SIG或第三方厂商制定。每个Profile约束着设备厂商在使用BLE通信时需要遵循的规范,使得终端用户能够无缝地使用产品之间的BLE通信功能,即实现互操作性。

例如,假设有一个心率测量Profile,它必须包含一些特定的服务和特征,如Measurement、Body Sensor Location、Heart Rate Control Point等,以便不同厂家的设备之间可以快速且可靠地交换有效的心率数据。

因此,可以认为,Profile是建立在GATT之上的应用层。由于GATT协议灵活、可扩展且适用范围广,因此可以很容易地创建自定义Profile,以满足开发人员的特定需求。

那ATT又是啥?

ATT(Attribute Protocol)是BLE协议栈中负责管理设备间数据读写和交换的底层协议之一。在BLE通信中,一个设备可以做出Server或Client的角色。Server会包含若干个Service,每个Service中又包含多个Characteristic;而Client则可以通过这些Service和Characteristic进行读写、变更等操作。

ATT通过GATT来确定数据格式和属性,以实现不同设备间信息的传输和交换。当Server需要传输特定Service或Characteristic时,必须先将该Service或Characteristic分配一个唯一的UUID和Handle ID。由于ATT与GATT紧密结合,所以UUID和Handle ID也由它们统一管理。

对于一个BLE设备而言,所有可供访问的数据都被保存在Server端,通过连接后的数据通道来访问;而Client仅仅是一个访问Server上的 characteristic 的客户端。因此,在客户端向服务端发起数据请求时,使用ATT命令数据包传输数据、读取或写入Service和Characteristic是很常见的操作。

总之,ATT是低功耗蓝牙技术中重要的通信协议之一,主要负责规定了 Server 和 Client 之间的数据访问协议规范。其实现方式从底层确保了 BLE 数据传输的稳定和可靠性,并且可扩展性非常好,适用于多种各种类型的 BLE 设备。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aLEESSox-1686832458361)(null)]

什么是MTU协商

MTU(Maximum Transmission Unit)是指BLE连接中可用于一次性传输的最大字节数。MTU Negotiation(MTU协商)是指在BLE设备之间建立连接时,通过交换ATT MTU Request和ATT MTU Response消息,协商确定两台设备之间MTU的大小。

实际上,在建立BLE连接时,服务器会发送ATT MTU Request消息,要求客户端回复其支持的最大MTU大小。而客户端则会根据实际情况回复ATT MTU Response消息,并将自己支持的最大MTU大小告知服务器。在双方完成协商后,BLE连接使用的MTU大小就确定下来了,从而可以更高效地进行数据传输。

需要注意的是,BLE连接所使用的MTU大小可能受到设备硬件、协议栈以及操作系统等因素的影响,不同设备的MTU大小可能会有所差异。因此,编写BLE应用程序时,需要根据实际情况进行MTU设置和优化,以尽量提高数据传输效率。

蓝牙主机和从机的概念

蓝牙主机和从机是指在蓝牙通信中连接的两个设备之间的关系。

蓝牙主机(Bluetooth Host)是发起连接请求并控制通信链路的设备,也可以被称为“主设备”或“主机”。它负责与蓝牙从机建立连接、管理通信过程以及控制数据传输。

蓝牙从机(Bluetooth Peripheral)是响应主机连接请求并接受连接的设备,也可以被称为“从设备”或“外围设备”。它与主机建立连接后会等待传入的数据,并向主机发送响应。从机可以是各种类型的设备,包括无线耳机、智能手表、智能家居设备等等。

需要注意的是,在一次蓝牙通信中,一个主机可以连接多个从机,但一个从机只能连接一个主机。

ble主机和从机如何建立连接

BLE从机与主机建立连接需要经过以下步骤:

  1. 从机广播:从机不断发送广播包告诉主机自己的存在和可用性。

  2. 主机扫描:主机搜索附近的从机并扫描它们发出的广播包,以确定可用设备。

  3. 主机请求连接:主机选择想要连接的从机并向其发送连接请求。

  4. 从机响应连接请求:如果从机接受连接请求,它会返回一个连接确认。

  5. 安全配对:此时如果有安全配对需求,则主机和从机进行配对操作(加密等)。

  6. 建立连接:在完成了上述步骤之后,主机和从机就成功建立起了连接,并可以进行数据传输。

需要注意的是,BLE从机和主机之间的连接具有时限,如果长时间没有数据传输,连接可能会自动中断以节省电量。若需要维持连接,请在一定时间内进行数据交互以保持连接。

蓝牙地址

蓝牙地址,也称作 Bluetooth MAC (Media Access Control) 地址,是一个48位的唯一硬件标识符,用于在蓝牙设备之间建立连接和通信。它由全球唯一的组织,即 IEEE(Institute of Electrical and Electronics Engineers)负责管理分配。

蓝牙地址通常表示为 12 个十六进制数(例如:00:11:22:33:44:55),其中前6个数字代表蓝牙适配器的厂商 ID,后6个数字是该适配器的独特序列号。蓝牙地址不同于 IP 地址,它们只在网络层次结构上唯一标识设备,而蓝牙地址则更加接近于物理层面上的设备地址。

需要注意的是,在蓝牙通讯过程中,设备不是直接使用蓝牙地址相互通信,而是通过蓝牙协议栈上的 L2CAP(Logical Link Control and Adaption Protocol)层进行通信,L2CAP 层使用其自己的 Channel ID 和 Connection Handle 来标识正在交换数据的蓝牙设备。

BLE配对的相关概念

Paring(配对)和bonding(绑定)是实现蓝牙射频通信安全的一种机制,有两点需要注意:

  • 一是paring/bonding实现的是蓝牙链路层的安全,对应用来说完全透明,也就是说,不管有没有paring/bonding,你发送或接收应用数据的方式是一样的,不会因为加了paring/bonding应用数据传输需要做某些特殊处理;

  • 二安全有两种选项:加密或者签名,目前绝大多数应用都是选择加密,后续我们也会以加密为重点进行讲述

实现蓝牙通信安全,除了paring/bonding这种底层方式,用户也可以在应用层去实现相同功能,两者从功能上和安全性上没有本质区别,只不过应用层自己实现的话,需要自己选择密码算法,密钥生成,密钥交换等,如果你不是这方面的专家,你的应用就有可能会存在安全漏洞。Paring/bonding则把上述过程标准化,放在了蓝牙协议栈中,并且其安全性得到了充分评估,用户可以 “无感的” 用上安全的蓝牙通信。

Paring/bonding是蓝牙security manager(SM)的一部分,SM定义了蓝牙通信的安全框架,里面涉及安全架构,密码工具箱,paring协议等,其中paring协议是关键,所以我们经常把paring和SM二者等价,下面将对paring进行详细阐述。

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

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

相关文章

tftp服务器环境搭建与nfs服务器环境搭建

目录 tftp 服务器环境搭建 实验步骤: nfs 服务器环境搭建 实验步骤 tftp 服务器环境搭建 实验步骤: 一、 tftp 服务器环境搭建 1 、 打开一个命令行终端,执行如下命令查看是否已安装 tftp 服务器 $ dpkg -s tftpd-hpa 若显示如下信…

vue可视化面板创建项目

前端项目初始化步骤 安装 vue 脚手架 通过vue脚手架创建项目 在命令行输入vue ui 等待可视化界面打开 填写项目名称(必须英文)和仓库信息(可选填),然后点击下一步进入“预设面板” 这里根据需要选择一个选项,然后…

Node.js入门之 - 初识Node.js

初识 Node.js 1. 起源 Node.js 起源于 2009 年,由 Ryan Dahl 开发,起初的目的是为了解决一些网络应用运行缓慢的问题。 在 Node.js 之前,一般会采用 LAMP(Linux Apache MySQL PHP)或者 MEAN等技术栈开发 web 应用。这些技术通常会采用请求-响应模型: 客户端(浏览器)发送一…

机器学习 day19(使用python和np实现前向传播)

烤咖啡豆模型 使用一维数组来表示这些向量和参数,所以只有一个方括号W1_1:表示layer 1的第一个神经元的WZ1_1:表示 W1_1和输入X之间的点积,再与b1_1相加a1_1:表示应用Z1_1的sigmoid函数a1:表示把a1_1&…

Explain和索引基本优化示例

一、Explain介绍 1、Explain不用版本的使用 在mysql8.0版本只能用explain,已经弃用了explain extended和explain partitions,用了都会出现语法问题,只能用explain;在explain语句后面加上show warnings;可以查看mysql优化后的语句…

市场·分析

寡头垄断市场 完全竞争市场 完全垄断 垄断竞争 博弈论与寡头竞争理论 寡头市场的特征: 少量的企业竞争策略互动纯寡头 -生产相同产品的企业 -市场上只有一个价格差异化寡头 -生产差异化产品的企业 -价格成为决策变量 博弈论基础 博弈论模型描述个体在知道他所采…

用flex布局实现一个流程设计器

最近接到一个需求,要做一个流程设计的功能,大概长下面这个样子: 支持添加、编辑和删除节点,节点只有四种类型:开始节点、普通节点、分支节点、结束节点。 因为每个节点只有一个进和一个出,且节点不需要支持…

一文扫盲 OA、CRM、ERP、MES、HRM、SCM、WMS、KMS 等B端系统

OA系统 (Office Automation System,办公自动化系统):OA系统是一种用于协调、管理和优化办公流程的软件系统,包括电子邮件、日程安排、文档管理、工作流程管理等功能模块,帮助企业提高工作效率和管理水平。…

C#程序的内存映射文件解析

一、背景 前段时间训练营里有朋友问 内存映射文件 是怎么玩的?说实话这东西理论我相信很多朋友都知道,就是将文件映射到进程的虚拟地址,说起来很容易,那如何让大家眼见为实呢?可能会难倒很多人,所以这篇我…

《项目实战》构建SpringCloud alibaba项目

文章目录 1、概要2、整体架构流程2.1、技术结构组成部分 3、技术名词解释4、技术细节4.1、构建父工程4.1.1、选择构建Maven项目4.1.2、修改父工程文件4.1.3、修改父工程pom.xml配置4.1.3.1、添加springboot支持4.1.3.2、修改JDK版本、编码、springboot版本配置4.1.3.3、添加Spr…

自定义MaterialEditText

自定义MaterialEditText 日记 现在都不流行写博客了,因为这玩意都认为对于面试没啥用,我感觉很多事情不应该太功利。所谓博客还是更多的应该用来进行自己日常学习的归纳和总结,而不是去贪图所谓的面试加分。因为面试可能是一时的&#xff0…

Apple Vision Pro的价格并没有看起来那么疯狂

When Apple announced the price of their groundbreaking new mixed reality headset, the Vision Pro, jaws around the world collectively dropped. At a hefty $3,499, it’s not for everyone, but is it really so unreasonable if we take a closer look? 当苹果宣布其…

CSS特性、背景属性和显示模式

CSS特性 CSS特性:化简代码 / 定位问题,并解决问题 继承性层叠性优先级 继承性 继承性:子级默认继承父级的文字控制属性。 注意:如果标签有默认文字样式会继承失败。 例如:a 标签的颜色、标题的字体大小。 层叠性 …

前端 sentry 接入钉钉机器人

sentry 接入钉钉机器人 打开钉钉,添加机器人 此时会得到Webhook地址,记录一下,以后会用到 sentry 端设置 看看这里有木有钉钉插件,有的话开启插件,并配置这里我说一下没有的情况下,我们何如设置 这里需要填写webhook url 这个的url 需要是一个公网的地址,不可以是本地…

HID协议学习

HID协议学习 0. 文档资料 USB_HID协议中文版_USB接口HID设备_AUJsRmB9kg.pdf HID报告描述符精细说明_mgCxM8_ci9.pdf hut1_22_U3cvnwn_ZZ.pdf 1. 基本概念 HID协议是一种基于USB的通讯协议,用于在计算机和输入设备之间进行数据传输。HID协议定义了标准的数据格…

动态规划算法(子数组专题1)

动态规划算法专辑之子数组问题(1) 本专栏将从状态定义、状态转移方程、初始化、填表顺序、返回值这五大细节来详细讲述动态规划的算法的解题思路及代码实现一、什么是子数组 子数组:子数组是数组中的一个连续部分的集合,子序列可…

Python+Selenium UI自动化测试环境搭建及使用

目录 一、什么是Selenium ? 二、Selenium环境搭建 三、WebDriver API 总结: 一、什么是Selenium ? Selenium 是一个浏览器自动化测试框架,它主要用于web应用程序的自动化测试,其主要特点如下:开源、免费…

缅怀(上次写博客是2009年10月24日)

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

Nucleo-F411RE (STM32F411)LL库体验 3 - 滴嗒定时器的配置

Nucleo-F411RE (STM32F411)LL库体验 3 - 滴嗒定时器的配置 1、LL库延时 LL库初始化时钟的时候调用了LL_Init1msTick(100000000)函数,这个函数其实就是初始化了系统的滴答定时器。 LL_InitTick原型如下: load值 sysclk/1000&a…

RocketMQ架构和工作流程

一.MQ概述 1.简介 MQ,Message Queue,是一种提供消息队列服务的中间件,也称为消息中间件,是一套提供了消息生产、存储、消费全过程API的软件系统。消息即数据。一般消息的体量不会很大。 2.用途 限流削峰 MQ可以将系统的超量请求…