ESP32外设的简单使用

arduino的iic通信库函数详解 iic库中包含的函数如下: 1 Wire.begin() 2 Wire.requestFrom() 3 Wire.beginTransmission() 4 Wire.endTransmission() 5 Wire.write() 6 Wire.available() 7 Wire.read() 8 Wire.onReceive() 9 Wire.onRequest() Wire.begin() 阅读详情

前言
本文是博主在准备全国大学生物联网设计竞赛(获得国一)作品时,以项目需求驱动的形式,根据需要用到的内容学习整理成的文档(并非所有内容,部分我自己没有搞懂的内容没放上来),仅涉及ESP32的简单使用,开发环境为Arduino(目前正在系统学习esp-idf开发,后面会放上来),希望对各位有帮助。

WiFi

下面是一些 ESP32 Arduino 库中常用的 Wi-Fi 相关函数的介绍:

  1. WiFi.begin(ssid, password):该函数用于连接到 Wi-Fi 网络。需要提供要连接的网络的 SSID 和密码作为参数。
  2. WiFi.disconnect():该函数用于断开当前的 Wi-Fi 连接。
  3. WiFi.status():该函数返回当前 Wi-Fi 连接的状态。返回值可能是以下之一:
    • WL_CONNECTED:已连接到 Wi-Fi 网络。
    • WL_DISCONNECTED:未连接到 Wi-Fi 网络。
    • WL_IDLE_STATUS:Wi-Fi 处于空闲状态。
    • WL_NO_SSID_AVAIL:未找到指定的 Wi-Fi 网络。
  4. WiFi.localIP():该函数返回 ESP32 设备在 Wi-Fi 网络中分配的本地 IP 地址。
  5. WiFi.macAddress():该函数返回 ESP32 设备的 MAC 地址。
  6. WiFi.scanNetworks():该函数用于扫描周围可用的 Wi-Fi 网络。它返回一个整数,表示扫描到的网络数量。可以使用其他函数(如 WiFi.SSID() 和 WiFi.RSSI())来获取每个网络的详细信息。
  7. WiFi.SSID(networkIndex):该函数返回指定索引的扫描到的 Wi-Fi 网络的 SSID。
  8. WiFi.RSSI(networkIndex):该函数返回指定索引的扫描到的 Wi-Fi 网络的信号强度(RSSI)。

连接WiFi

#include <Arduino.h>
#include <WiFi.h>

// WiFi网络的SSID和密码
const char *ssid = "Xiaomi 6";
const char *password = "12345678";

// 定义LED引脚
#define LED_pin 2

void setup() 
{
  // 初始化串口通信,波特率为9600
  Serial.begin(9600);

  // 连接到WiFi
  WiFi.begin(ssid, password);

  // 输出连接状态信息到串口
  Serial.println("正在连接WiFi");

  // 等待WiFi连接成功
  while(WiFi.status() != WL_CONNECTED)
  {
    delay(500);           // 延迟500毫秒
    Serial.print(".");    // 输出"."表示等待
  }

  // WiFi连接成功后的信息输出
  Serial.print("连接成功");              // 输出连接成功提示
  Serial.print("IP:");                   // 输出IP地址提示
  Serial.println(WiFi.localIP());        // 输出设备的IP地址

  // 配置LED引脚模式为输出模式
  pinMode(LED_pin, OUTPUT);

  // 进行LED闪烁以指示连接成功
  digitalWrite(LED_pin, HIGH);  // 点亮LED
  delay(100);                   // 延迟100毫秒
  digitalWrite(LED_pin, LOW);   // 关闭LED
  delay(1000);                  // 延迟1秒
  digitalWrite(LED_pin, HIGH);  // 再次点亮LED
}

void loop() {
  // 主循环代码可以在此添加,目前为空
}

串口通信

UART 使用两根不同的数据线,一根用于发送数据(TX),另一根用于接收数据(RX)。UART 一般与其它外围设备(如鼠标、打印机和外部存储设备)连接,允许从计算机发送和接收数据。 UART 是一种可配置的接口,允许用户配置各种通信参数,如传输速率(称为波特率)、数据位数、奇偶校验、停止位和流控制。UART 也可以采用其它的通信协议,比如 RS-232 、 RS-422 和 RS-485 。

ESP32 提供了三个工作在 3.3V TTL 电平的通用同步接收器和发送器 (UART):UART0、UART1 和 UART2。这三个串行接口是硬件支持的。它们每个都暴露 4 个引脚:RX、TX、RTS 和 CTS。然而,Arduino IDE 仅使用 RX 和 TX 引脚。它们每个都分配有默认的 GPIO,如下表:

  • RTS(请求发送)引脚和CTS(清除发送)引脚:用于流控制。这些引脚在ESP32硬件上是可用的,但在Arduino IDE中通常只使用RX和TX引脚。
UART0UART1UART2
TX11017
RX3916

UART0 用于下载和 REPL(交互式解释器) 调试,UART1 用于模块内部连接 FLASH,通常也不使用,因此可以使用 UART2 与外部串口设备进行通信

Serial1.---
//串口1相关操作

Serial2.---
//串口2相关操作

Serial.begin();//配置串口
Serial.print();//打印信息
Serial.read();//读取一个字节的数据
Serial.available();//检查是否有可用数据(返回可用字节数)

//串口的开启,这里还可以传一些别的参数,下面四个最重要的:波特率,默认SERIAL_8N1为8位数据位、无校验、1位停止位,后面两个分别为 RXD,TXD 引脚
Serial2.begin(115200, SERIAL_8N1, 14, 15);

在 ESP32 中,除了 SERIAL_8N1 之外,还可以使用以下配置参数来设置串口通信的格式:

  • SERIAL_8E1:8 个数据位,偶校验位,1 个停止位
  • SERIAL_8O1:8 个数据位,奇校验位,1 个停止位
  • SERIAL_8N2:8 个数据位,无校验位,2 个停止位
  • SERIAL_8E2:8 个数据位,偶校验位,2 个停止位
  • SERIAL_8O2:8 个数据位,奇校验位,2 个停止位
    这些参数可以在调用 Serial.begin()函数时作为第二个参数传递,以配置串口通信的格式。

UATR1 的使用

UART1的默认引脚是GPIO9和GPIO10,但在ESP32硬件设计中,这些引脚被用来连接SPI闪存(用于存储程序代码和数据),导致它们无法直接用作UART的TX和RX引脚。为了在Arduino IDE中使用UART1,必须在代码中手动重新定义UART1的TX和RX引脚,选择不影响其他功能的GPIO引脚,以避免与SPI闪存冲突。

  1. 加载 HardwareSerial.h 库
#include <HardwareSerial.h>
  1. 创建一个 HardwareSerial 实例对象
HardwareSerial SerialPort(1);//use Uart1
  1. 初始化 SerialPort
    在 void setup() 中初始化 SerialPort
//初始化串口,并重新定义引脚
//函数格式如下
Serial1.begin(115200, SERIAL_8N1, new_rx_pin, new_tx_pin);

//参数包括串行通信的波特率、串行模式、使用的RX、TX引脚
SerialPort.begin(115200, SERIAL_8N1, 4, 2);
//这里把4设为RX,2设为TX

这里将 GPIO4 重新分配为 RX 引脚,将 GPIO2 重新分配为 TX 引脚
4. 使用 SerialPort
在代码的其它地方,SerialPort 的使用方法和 Serial、Serial2 的方法相同

在定义 SerialPort 以外的文件使用 SerialPort,需要使用 extern 关键字进行声明
extern HardwareSerial SerialPort;

串口输出相关功能

Serial.write()

print 和 write 的区别
Serial.print: 用于发送数据以人类可读的形式输出。它会将数据格式化为字符串并发送。例如,如果你发送一个整数 123,它会被转换为字符串 “123” 并发送。通常用于打印调试信息。

Serial.write: 直接发送二进制数据。它不会对数据进行任何格式化。例如,如果你发送整数 123,它会作为单个字节值(ASCII字符)发送,而不是字符串 “123”。通常用于发送原始数据或控制字符。

在 Arduino 中,Serial.write 的参数有以下几种:
Serial.write(val)val 是一个字节(byte)或字符(char)。
Serial.write(buffer, length) buffer 是一个存储要发送数据的字节数组,length 是数组的长度。

清除屏幕

Serial.print("\033[2J\033[H");  // ANSI序列,清除屏幕并将光标移动到左上角

ANSI 转义序列(由 GPT-4o 生成)
ANSI 转义序列是一种用于控制文本终端行为的特殊字符序列。这些序列通常以 \033(也可以写成 \e 或 \x1B)开头,后面跟着用于控制终端的具体指令。

具体指令解释

\033[2J:
\033 是 ANSI 转义序列的起始字符,表示开始一个控制序列。
[ 表示序列的开始。
2J 是指令的一部分,用于清除屏幕。
2J 的具体含义是清除整个屏幕。数字 2 表示清除整个屏幕,J 表示擦除命令。
所以 \033[2J 的效果是清除整个屏幕上的内容。

\033[H:
\033 是 ANSI 转义序列的起始字符。
[ 表示序列的开始。
H 是指令的一部分,用于将光标移动到屏幕的左上角位置。
所以 \033[H 的效果是将光标移动到屏幕的左上角。

总体原理
当你使用 Serial.print(“\033[2J\033[H”); 时,Arduino 的串口模块会发送这个字符序列到连接的串口设备(比如串口监视器软件或其他终端设备)。如果目标设备支持 ANSI 转义序列(如大多数终端模拟器和串口监视器软件),它会解析这个序列并执行相应的操作:
首先,\033[2J 序列告诉终端清除屏幕上的所有内容。
然后,\033[H 序列告诉终端将光标移动到左上角的位置(通常是第一行第一列)。
这样一来,屏幕上的所有内容都会被清除,并且光标会回到屏幕的起始位置,以便进行新的输出。

注意事项
ANSI 转义序列的可移植性很好,但不是所有终端软件都完全支持所有的 ANSI 控制序列。大多数现代终端仿真器和串口监视器支持基本的 ANSI 转义序列,但在特定的硬件或软件环境中可能会有所不同。
在使用 ANSI 转义序列时,确保目标设备和串口通信设置(如波特率和数据位)正确配置,以确保命令能够被正确解释和执行。
通过理解和利用 ANSI 转义序列,你可以在串口通信中实现各种控制和显示效果,这对于调试和交互式控制非常有用。

I2C

ESP32有2个硬件I2C总线接口,接口可以配置为主机或从机模式,支持如下特性:

  • 标准模式 (100 Kbit/s)
  • 快速模式 (400 Kbit/s)
  • 高达 5 MHz,但受 SDA 上拉强度的限制
  • 7位/10位寻址模式
  • 双寻址模式,用户可以通过编程命令寄存器来控制 I²C 接口,让他们有更大的灵活性

引脚定义

默认引脚

  • GPIO21作为SDA
  • GPIO22作为SCL
    这些引脚是可配置的,可以在代码中通过 Wire.begin(SDA, SCL); 函数来更改I2C接口的SDA和SCL引脚
bool begin(int sda, int scl, uint32_t frequency = 0U)
  • sda(类型:int):
    • 指定I2C数据线(SDA)的GPIO引脚编号。
  • scl(类型:int):
    • 指定I2C时钟线(SCL)的GPIO引脚编号。
  • frequency(类型:uint32_t,默认为0U):
    • 设置I2C的时钟频率(即波特率),单位为赫兹(Hz)。
    • 可选值通常包括100000(100 KHz,标准模式)和400000(400 KHz,快速模式)。
    • 如果未指定频率,默认频率将根据硬件特性自动设定。
  • 返回值(类型:bool):
    • 如果I2C初始化成功,返回true;否则返回false。

启动I2C
启动Wire库并作为主机或者从机加入总线,这个函数调用一次即可,参数为7位从机地址,不带参数就以主机的形式加入总线。

Wire.begin();
Wire.begin(address)

Wire.begin 是一个重载函数。在 Wire 库中,begin 函数有多个重载版本,以支持不同的初始化方式:

  1. Wire.begin():
    • 无参数调用,将设备配置为主设备模式。
    • 使用默认的I2C引脚和标准频率,适用于常规I2C主机模式初始化。
  2. Wire.begin(int address):
    • 传入从设备地址,将设备配置为从设备模式。
    • 参数 address 是从设备的7位I2C地址。使用此版本时,设备作为从机运行,响应其他主设备的请求。
  3. Wire.begin(int sda, int scl, uint32_t frequency = 0U)(仅适用于ESP32等支持自定义I2C引脚的设备):
    • 接收三个参数:SDA引脚、SCL引脚和可选的频率参数。
    • 用于主设备模式下自定义I2C引脚和频率,适合ESP32这种I2C引脚灵活配置的设备。

主设备从从设备请求字节
由主设备向从设备请求字节,之后用available()和read()函数读取字节,第三个参数位为stop,在请求后会发送停止消息,释放I2C总线,否则总线就不会被释放。

Wire.requestFrom(address, quantity);
Wire.requestFrom(address, quantity, stop);

address指定从设备的I2C地址(7位)。
quantity 请求的数据字节数。
stop 可选参数,为布尔值,默认为 true。true 表示传输完成后发送停止条件(终止通信),false 则保持连接,允许在同一会话中继续通信

给指定地址的从设备传输数据
给指定地址的从设备传输数据,之后调用write()函数排队传输字节,要通过endTransmission()结束传输。

Wire.beginTransmission(address);

uint8_t endTransmission(bool sendStop);
//true表示传输完成后发送停止条件,释放I2C总线。
//false表示保持I2C总线连接,以便继续进行下一次传输(用于多字节或多条指令的连续通信)

endTransmission()有以下几个返回结果:

  • 0:成功
  • 1:数据太长,无法放入发送缓冲区
  • 2:在发送地址时收到 NACK
  • 3:在发送数据时收到 NACK
  • 4:其他错误

写数据
向从设备写入数据,在调用 beginTransmission() 和 endTransmission() 之间。

Wire.write(value);      // 发送一个字节的数值数据(value)给I2C从设备
Wire.write(string);     // 发送一个字符串(string)数据给I2C从设备,字符串会逐字节发送直到字符串结束
Wire.write(data, length);  // 发送一个字节数组(data)给I2C从设备,数组长度由length指定
  • Wire.write(value):用于发送单个字节数据。value可以是0-255之间的整数或单个字符。
  • Wire.write(string):用于发送字符串数据,字符串会逐字节发送,直到遇到字符串结束符\0。
  • Wire.write(data, length):用于发送一个字节数组,data是指向数据的指针,length是发送的字节数。适合发送多字节数据,例如多个传感器值或字符串片段。

举个例子

#include <Wire.h>
byte val = 0;// 无符号的8位整数

void setup()
{
  Wire.begin(); // 加入 I2C 总线
}

void loop()
{
  Wire.beginTransmission(44); // 开始传输到设备 #44 (0x2C)
                              // 设备地址在数据手册中指定
  Wire.write(val);            // 发送字节值 val
  Wire.endTransmission();      // 停止传输

  val++;        // 增加 val 的值
  if(val == 64) // 如果达到了第 64 个位置 (最大值)
  {
    val = 0;    // 从最小值重新开始
  }
  delay(500);   // 延迟 500 毫秒
}

读数据
调用requestFrom()后从从设备读取数据。

uint8_t requestFrom(int address, int size);
Wire.read()
  • address:指定从设备的I2C地址(7位地址)。
  • size:请求的数据字节数,即希望接收的数据字节数量。
  • 返回值:uint8_t 类型,表示成功接收的字节数。如果返回的字节数小于size,则表示读取操作没有获取到足够的数据(允许用户检查是否请求的数据数量和接收到的数据数量一致)。
  • requestFrom() 并不返回实际的数据,而是请求数据并将其放入一个缓冲区中,等待读取。

举个例子

#include <Wire.h>

void setup()
{
  Wire.begin();        // 加入 I2C 总线
  Serial.begin(9600);  // 启动串口通信,波特率为9600,用于输出数据
}

void loop()
{
  Wire.requestFrom(2, 6);    // 向地址为2的从设备请求6字节数据
  while(Wire.available())    // 从设备可能会发送少于请求的数据
  {    
    char c = Wire.read();    // 接收一个字节并将其作为字符
    Serial.print(c);         // 打印字符到串口
  }

  delay(500);  // 延迟500毫秒
}

I2C地址搜索

#include <Arduino.h>
#include <Wire.h>

void setup() 
{
  Wire.begin();  // 初始化I2C总线

  Serial.begin(115200);  // 初始化串口通信,波特率为115200
  while (!Serial);  // 等待串口连接成功

  Serial.println("\nI2C Scanner");  // 输出提示信息到串口,表明程序启动
}

void loop() 
{
  byte error, address;  // 错误代码和I2C设备地址
  int deviceCount = 0;  // 设备计数器,用于记录找到的I2C设备数量

  Serial.println("Scanning...");  // 向串口输出正在扫描的提示信息

  // 扫描I2C地址范围,从1到127
  for (address = 1; address < 127; address++) 
  {
    Wire.beginTransmission(address);  // 向当前地址发送I2C传输开始信号
    error = Wire.endTransmission();  // 结束传输并获取错误码

    // 检查传输结果,如果没有错误则表示发现设备
    if (error == 0) 
    {
      Serial.print("I2C device found at address 0x");  // 输出已找到设备的提示信息
      if (address < 16) Serial.print("0");  // 地址小于16时补充0以保持格式一致
      Serial.print(address, HEX);  // 以16进制格式输出设备地址
      deviceCount++;  // 增加设备计数
    } 
    else if (error == 4) // 错误代码4表示找到设备但发生未知错误
    {
      Serial.print("Unknown error at address 0x");  // 输出地址未知错误信息
      if (address < 16) Serial.print("0");  // 地址小于16时补充0以保持格式一致
      Serial.println(address, HEX);  // 以16进制格式输出地址
    }
  }

  // 根据扫描结果输出总结信息
  if (deviceCount == 0) 
  {
    Serial.println("No I2C devices found\n");  // 如果未找到设备,输出相应信息
  } 
  else 
  {
    Serial.println("Scan complete\n");  // 如果找到设备,输出扫描完成信息
  }

  delay(5000);  // 等待5秒后再次进行扫描
}

micros()函数

在 Arduino 编程中,micros () 函数用于返回自从 Arduino 板启动以来经过的微秒数。它的精度和范围取决于具体的 Arduino 型号,在许多常见的Arduino板子上,micros() 的精度为 4 微秒,也就是说,每次调用的返回值会增加 4 的倍数。
这个函数不接受参数,返回一个 unsigned long 类型的值,表示自 Arduino 开始运行以来经过的微秒数,可用于一些需要定时触发的事件的标志。由于 unsigned long 的最大值限制(4294967295 微秒),micros() 的计数会在约 71 分钟后溢出,并从 0 重新开始。

unsigned long time = micros();

中断

在单片机中,中断是指当 CPU 在正常处理主程序时,突然发生了另一件事件 A(中断发生)需要 CPU 去处理,这时 CPU 就会暂停处理主程序(中断响应),转而去处理事件 A(中断服务)。当事件 A 处理完以后,再回到主程序原来中断的地方继续执行主程序(中断返回)。这一整个过程称为中断
中断的嵌套:在一个中断过程中,发生了一个更高级别的中断事件,CUP优先处理高优先级中断

硬件中断:也被称为外部中断,硬件中断响应外部硬件事件而发生。例如,当检测到触摸时会发生触摸中断,而当 GPIO 引脚的状态发生变化时会发生 GPIO 中断。GPIO 中断和触摸中断属于这一类
软件中断:当触发软件事件(例如定时器溢出)时,会发生这种类型的中断。定时器中断是软件中断的一个例子

外部中断

可以通过外部中断等方式,避免对按键等的重复扫描,从而节省 CPU 资源
ESP 32 的外部中断有上升沿、下降沿、低电平、高电平触发模式。

比如下降沿触发就是当按键按下后触发中断

程序配置

Arduino 中的外部中断配置函数

void attachInterrupt(digitalPinToInterrupt(pin), ISR, mode) 

包括 3 个参数:
Pin GPIO 端口号
ISR 中断服务程序,没有参数与返回值的函数
Mode 中断触发的方式,支持以下触发方式

  • LOW 低电平触发
  • HIGH 高电平触发
  • RISING 上升沿触发
  • FALLING 下降沿触发
  • CHANGE 电平变化触发

注意事项

  1. 尽量保证中断程序内容少避免在中断处理函数中使用阻塞函数(如 delay ()),使用非阻塞的延迟方法来处理需要延迟的操作(micros () 函数),以保证中断的正常执行和系统的稳定性
    这是因为 delay () 函数会阻塞整个系统,包括中断的正常执行
  2. 当中断触发时,处理函数应该尽快执行完毕,以确保及时响应并避免中断积压
  3. 与主程序共享的变量要加上 volatile 关键字
  4. 在 Arduino 中使用中断时,应尽量避免在中断处理函数中使用 Serial 对象的打印函数
    需要在中断中对数据进行调试时,可以通过在中断中改变标记位,再在主函数中打印的方法
    当在中断处理函数中使用 Serial 打印函数时,会导致以下问题:时间延迟:Serial 打印函数通常是比较耗时的操作,它会阻塞中断的执行时间,导致中断响应的延迟。这可能会导致在中断期间丢失其他重要的中断事件或导致系统不稳定。缓冲区溢出:Serial 对象在内部使用一个缓冲区来存储要发送的数据。如果在中断处理函数中频繁调用 Serial 打印函数,可能会导致缓冲区溢出,造成数据丢失或不可预测的行为

定时器中断

在使用 Arduino 操控 ESP-32 时,定时器分为硬件定时器和软件定时器

硬件定时器

硬件定时器是 ESP-32 芯片上的内置计时器,它们是专门设计用于定时和计时任务的硬件模块。硬件定时器可以通过设置特定的寄存器来配置和控制,通常具有更高的精确度和稳定性。它们不受软件的影响,可以在后台独立运行,不会受到其他代码的干扰。硬件定时器适用于需要高精度和实时性的定时任务,例如 PWM 输出、捕获输入脉冲等
ESP-32 具有 4 个硬件定时器,具体需要参考技术文档

程序配置

初始化硬件定时器

void timerBegin(timer_num_t timer_num, uint32_t divider, bool count_up);

//eg:
hw_timer_t* timer = NULL;//在Arduino库中一定要用hw_timer_t* 这个类型
timer = timerBegin(0, 80, True);
  • timer_num定时器编号,可选值为 0-3 等。
  • divider定时器的分频系数,用于设置定时器的时钟频率。较大的分频系数将降低定时器的时钟频率。可以根据需要选择合适的值,一般设置为 80 即可;
  • count_up指定定时器是否为向上计数模式。设置为 true 表示向上计数,设置为 false 表示向下计数。

将中断处理函数与特定定时器关联

void timerAttachInterrupt(hw_timer_t *timer, void (*isr)(void *), void *arg, int intr_type);

//eg:
void timerAttachInterrupt(timer, timer_interrupt, NULL, true);
  • timer定时器指针
  • isr中断处理函数
  • arg传递给中断处理函数的参数
  • intr_type中断类型,可选值为 true(边沿触发)或 false(电平触发)
    边沿触发指的是中断在信号的变化(即上升沿或下降沿)时触发。例如,当信号从低电平变为高电平(上升沿)或者从高电平变为低电平(下降沿)时触发中断。边沿触发适用于捕捉信号变化的瞬间。(用于捕捉事件的快速变化)
    电平触发指的是中断在信号处于某个特定电平(高电平或低电平)时触发。例如,当信号维持在高电平或低电平时,持续触发中断。电平触发适用于检测信号是否处于某个稳定状态。

设定定时器的计数值(事件间隔)

void timerAlarmWrite(hw_timer_t *timer, uint64_t alarm_value, bool autoreload);
//事件单位为μs

//eg:
void timerAlarmWrite(timer, 1000000, true);
  • timer定时器指针
  • alarm_value:定时器的计数值,即触发时间间隔
  • autoreload是否自动重载计数值,可选值为 true(自动重载)或 false(单次触发)

其它

void timerAlarmEnable(hw_timer_t *timer);// 用于启动定时器,使其开始计数;
void timerAlarmDisable(hw_timer_t *timer);// 用于禁用定时器,停止计数;
bool timerGetAutoReload(hw_timer_t *timer);// 获取定时器是否自动重新加载;
// 获取定时器的自动重载模式状态,返回 true 表示自动重载,false 表示单次触发
uint64_t timerAlarmRead(hw_timer_t *timer);// 获取定时器计数器报警值;
void timerStart(hw_timer_t *timer);// 计数器开始计数;
void timerStop(hw_timer_t *timer);// 计数器停止计数;
void timerRestart(hw_timer_t *timer);// 计数器重新开始计数,从 0 开始;
bool timerStarted(hw_timer_t *timer);// 计数器是否开始计数
// 检查定时器是否已启动,返回 true 表示计数器正在计数,false 表示未启动
使用步骤
  1. 初始化定时器 timerBegin ()
  2. 注册中断处理函数 timerAttachInterrupt ()
  3. 设置定时器模式 timerAlarmWrite ()
  4. 启动定时器 timerAlarmEnable ()

蓝牙

基础知识

蓝牙分为 低功耗蓝牙 BLE 和 经典蓝牙 BT
BLE 是蓝牙 4.0 标准中的一个子集,也就是说,蓝牙 4.0 标准包含了 BLE 和经典蓝牙(BR/EDR)两种模式。BLE 是低功耗蓝牙,而经典蓝牙则是一种传统的蓝牙技术,适用于传输音频和文件等大数据量数据。
低功耗蓝牙 BLE 主打低功耗,多用于物联网
经典蓝牙 BT 主打近距离高速传输,多用于蓝牙耳机等
双模蓝牙 兼容 BLE 和 BT,ESP-32 支持双模蓝牙通信

[!note] 单模的 BLE 和单模的 BT 之间不能进行通信

在蓝牙通信中,设备分为主设备和从设备,主设备负责发起连接请求和管理连接状态,从设备在收到请求后进行确认和连接操作,主从设备之间可以是一对一或是一对多

在蓝牙连接中,从设备可以设置一个验证用的 PIN(配对码/密码),防止别人误连设备,在 ESP-32 中,可以使用 SerialBT.setPin() 设置

const char* pin = "1234";
SerialBT.setPin(pin);

主从设备都可以使用这个方法来设置PIN码,从设备使用这个方法设置的时候,就是要求发起链接的设备要输入这个PIN码。而主设备使用这个方法设置的时候,则表示将会使用这个PIN码去建立与从设备的连接

经典蓝牙 BT

经典蓝牙的使用方法类似异步串行通信 Serial

[!warning] 经典蓝牙在与手机、电脑通信时,需要保持串口打开状态,否则会显示“未连接”

#include <Arduino.h>
#include <BluetoothSerial.h>

#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)//检查蓝牙配置是否启动
#error Bluetooth is not enabled! Please run `make menuconfig` to and enabled it.//如果没有启动,则报错
#endif

BluetoothSerial SerialBT;//声明一个BluetoothSerial对象,通过这个对象,可以调用类中的方法来管理蓝牙通信

void setup()
{
 Serial.begin(115200);
 SerialBT.begin("ESP-32test");//设置设备名
}

void Loop()
{
 if (Serial.available())
  {
    SerialBT.write(Serial.read()); //将串口收到的数据,再通过蓝牙串口转发出去
  }
  if (SerialBT.available())
  {  //将蓝牙串口收到的数据,再通过串口把信息发回给电脑
    Serial.println(SerialBT.read());
  }
}

EPS-32 之间通过经典蓝牙通信

#include <Arduino.h>
#include "BluetoothSerial.h"

BluetoothSerial SerialBT;//声明一个BluetoothSerial对象,通过这个对象,可以调用类中的方法来管理蓝牙通信

#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)//检查蓝牙配置是否启动
#error Bluetooth is not enabled! Please run `make menuconfig` to and enabled it.//如果没有启动,则报错
#endif

#if !defined(CONFIG_BT_SPP_ENABLED)
#error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip.
#endif

#define Master 1 //1主机 0从机

void Bluetooth_Event(esp_spp_cb_event_t event, esp_spp_cb_param_t *param);

uint8_t address[6]={0xcc,0xdb,0xa7,0x30,0xea,0x06}; //从机MAC地址 不同的蓝牙地址不同 需要自己修改
//目前是00002的MAC地址

void setup()
{
  Serial.begin(115200);
  SerialBT.register_callback(Bluetooth_Event);//设置回调函数 连接、断开、发送、接收
  if(Master)
  {
    SerialBT.begin("Esp-32_Master", true);
    Serial.println("Init complete -Master.");
    SerialBT.connect(address);//尝试联机到给定地址的蓝牙设备
  }
  else
  {
    SerialBT.begin("Esp-32_slave");
    Serial.println("Init comlete -Slave.");
  }
  Serial.println("The device started, now you can pair it with bluetooth.");
}


void loop()
{
  if (SerialBT.available())
  {
    char c = SerialBT.read();
    Serial.println(c);
    SerialBT.write(toupper(c));
  }
  if(Master)
  {
    SerialBT.write('A');
    delay(500);
  }
}


void Bluetooth_Event(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)//蓝牙事件回调函数
{
  if(event == ESP_SPP_OPEN_EVT || event == ESP_SPP_SRV_OPEN_EVT)//蓝牙连接成功的标志
  {                                                             //蓝牙主机和从机模式对应的标志不同,前面是主机,后面是从机
    Serial.println("Connection successful.");
  }
  else if(event == ESP_SPP_DATA_IND_EVT)//数据接收标志
  {
    while(SerialBT.available())
    {
      Serial.println(SerialBT.read());
    }
    Serial.println("Receive complete.");
  }
  else if(event == ESP_SPP_CLOSE_EVT)//蓝牙连接断开标志
  {
    Serial.println("Disconnect successful.");
  }
  else if(event == ESP_SPP_WRITE_EVT)//数据发送标志
  {
    Serial.println("Send complete.");
  }
}
  • 通过修改 Master 的值,决定是主机还是从机模式
  • 使用 register_callback() 注册一个回调函数,当连接成功、断开连接、接收/发送数据时,回调函数将被启用
  • 回调函数的注册需要在 SerialBT.begin() 之前

可以在手机上通过 nRF connect APP 或者其它方式查看设备的 MAC 地址

Arduino中Wire类库介绍 对于iic总线的使用,Arduino IDE自带了一个第三方库Wrie。下面介绍Wrie成员函数。 1、begin() 功能:初始化iic连接,并作为主机或者从机设备加入iic总线。 语法: begin(address) 当没有填写参数时,设备会以主机模式加人IIC总线; 当填写了参数时,设备会以从机模式加入IIC总线,address可以设置为0~127中的任意地址。 begin() begin(address) // 当没有填写参数时,设备... 阅读详情

相关推荐

20- ESP32-S3 SPI 外设,主设备编程方法

ESP32-S3 芯片集成了四个SPI 控制器,分别为和SPI3。SPI0 和SPI1 控器主要供内部使用以访问外部 FLASH 和 PSRAM,所以只能使用SPI2 和 SPI3。SPI2 又称为HSPI,SPI3 又称为VSPI,这两个属于GP-SPI。特性描述支持模式主机模式和从机模式支持通信方式半双工通信和全双工通信支持的数据模式。

宁子希的博客 7804

I2C的使用

I2C的使用 I2C用通过总线通信将多个设备连接在一起,可以解决数据接口缺少的问题,是不同单片机间实现通信的一种方式 在arduino中设有SDA /SCL接口。这些接口在板子上有预定义的,当然也可以自己定义这种接口。 需查看所使用设备的电子线路图来设置SDA/SCL的通信接口。 WEMOS (SDA=D14=4 / SCL=D15=5) 在使用过程中分为主从。目前来看主端需要定义两个端口的PIN...

wyaspnet的专栏 1813

开始在ESP32上使用Python

使用Python可以有效的利用你的ESP32开发板,反之亦然,ESP32芯片是使用Python的绝佳平台,本教程将引导你创建Python,获取指示符,使用WebREPL,连接到网络并使用因特网通信,使用硬件外设并控制外设。 我们开始吧! 首先,您需要一块带有ESP32芯片的电路板。Python软件支持ESP32芯片本身,所以任何板子都可运行。 板子的主要特征是其FlashROM空间的大小、GPIO引脚与外界连接的方式以及其是否包括一个内置的USB串口转换器以便在您的电脑上使用UART。 FlashRO

qq_53733036的博客 1万+

《 Wire.h 》 库笔记

主到从:对于从主设备到从设备的传输,在调用Wire.beginTransmission()和Wire.endTransmission( )之间使用Wire.write()。使用:当主设备使用Wire.requestFrom()从从设备请求数据时,将调用此函数。用途:此函数用于结束由 beginTransmission()开始的到从设备的传输,并传输由Wire.write() 排队的字节。函数Wire.read()用于读取从设备发送的数据。()后从从设备传输到主设备,或者从主设备传输到从设备。

weixin_56137582的博客 2186

esp32_nat_router:ESP32的简单NAT路由器

ESP32 NAT路由器 这是使用ESP32作为WiFi NAT路由器的固件。 可以用作 适用于现有WiFi网络的简单范围扩展器 为访客或物联网设备设置具有不同SSID /密码的其他WiFi网络 它可以实现超过15mbps的带宽。 该代码基于和 。 表现 所有测试均使用IPv4和TCP协议。 木板 工具 优化 CPU频率 通量 力量 ESP32D0WDQ6 iperf3 0g 240MHz 16.0 MBits/s 1.6 W ESP32D0WDQ6 iperf3 0s 240MHz 10.0 MBits/s 1.8 W ESP32D0WDQ6 iperf3 0g 160MHz 15.2 MBits/s 1.4 W ESP32D0WDQ6 iperf3 0s 160MHz 14.1 MBits/s 1.5 W 第一次启动 首次启动后,ESP

esp32-nat-router :一个用ESP32实现的中继路由器

这是一个用ESP32实现的WiFi NAT路由器固件。它可以用于现有WiFi信号范围扩展,通过无线连接主路由,再把自身当作一个AP,终端通过无线连接AP实现无线上网,其实就相当于一个wifi信号放大器,可以为终端设备设置不同SSID/密码,它可以实现超过15mbps的带宽。该代码基于Console组件和esp idf nat示例。

ESP32-外设GPIO资料

ESP32 GPIO资料

事业不等于爱好 871

Proteus仿真实战:用AT89C51+L298N搭建直流电机PID调速系统(附完整工程文件)

本文详细介绍了使用Proteus仿真平台结合AT89C51单片机和L298N驱动模块搭建直流电机PID闭环调速系统的全过程。从硬件选型、电路连接到PID算法实现与参数整定,提供了完整的开发指南和工程文件,帮助读者快速掌握闭环调速系统的设计与调试技巧。

weixin_28724661的博客 448

老宇哥带你玩转ESP32:01入门介绍

接触物联网差不多10年了。先跟大家聊聊,老宇哥11年进入大学,大一就在实验室开始了电子研发,记得那时候师兄介绍我买了一块郭天祥老师的51开发板,还有配套的一本书,虽然从小非常喜欢电子,经常...

karaxiaoyu的专栏 6435

线性回归LinearRegression

对视频中线性回归算法的相关内容进行总结与记录 文章目录一、理论基础二、实例2.1 一元线性回归2.1.1 推导过程2.1.2 代码实现(基于numpy)2.1.3 代码实现(基于sklearn)2.2 多元线性回归2.2.1 代码实现(基于numpy)2.2.2 代码实现(基于sklearn) 一、理论基础 回归 通常是指利用某个函数,尽可能把数据样本点“串”在一起,用于描述输入变量和输出变量间的变化关系 线性回归 特点:用来把数据“串”起来的那个函数是线性的 (一元线性回归(一个自变量) &a.

邱邱邱的博客 1068

自动驾驶预测决策规划八股

预测规划八股个人总结。

dawushixiange的博客 479

esp32-C3

ESP32 系列包括芯片 ESP32-D0WDQ6(和 ESP32-D0WD), ESP32-D2WD 电子版, ESP32-S0WD系列, 以及系统级封装 (SiP) ESP32-PICO-D4。学习了ESP32的引脚部分后,了解各个引脚的作用,焊接一块电路板,并将ESP32-C3连接,安装好Arduino软件以及相关的库。闪存通过 ESP32-D2WD 和 ESP32-PICO-D4 上的 IO16、IO17、SD_CMD、SD_CLK、SD_DATA_0 和 SD_DATA_1 进行内部连接。

qq_58739767的博客 2251

vue3 - 超详细 cropper.js 实现图片(头像)裁剪效果功能,在vue3中上传图像后按比例进行图片的裁剪及实时预览,Element / Ant Desi(详细示例源码,一键复制开箱即用!)

vue3图片裁剪及实时预览,vue3 vue-cropper,vue3头像裁剪,vue3 el-upload+vue-cropper 图像图片头像裁剪上传,vue3怎么进行图片的裁剪,vue3使用vue-cropper图片裁剪,vue3项目如何实现头像图片裁剪上传功能,vue3网站pc怎么将上传的图片按比例进行裁剪,vue3+element plus实现图片上传按尺寸大小裁剪,vue3中图片上传使用cropperjs 头像裁剪,vue3中图片上传使用cropperjs 头像裁剪,vue3好用的图片裁剪插件组

✅ 精选推荐文章 4506

ESP32-BLE-CompositeHID:一款多功能的蓝牙设备模拟库

ESP32-BLE-CompositeHID:一款多功能的蓝牙设备模拟库 在物联网与智能家居日益普及的今天,开发一款能够同时模拟多种输入设备的解决方案显得尤为重要。ESP32-BLE-CompositeHID 正是这样一款功能强大的开源库。 项目介绍 ESP32-BLE-CompositeHID 是基于 ESP32 开发板的一个库,它能够让 ESP32 行为如同一个蓝牙鼠标、键盘、游戏手柄或这些设...

gitblog_00554的博客 1150

手把手教你完成基于matlab的路面裂缝检测系统设计

裂缝是路面病害最早的形态之一,只有在裂缝出现的早期进行维护,才能有效地提高公路的使用寿命。因此,路面裂缝的检测识别系统对于提升公路质量有着重大的意义。首先,对路面裂缝图像进行相应的预处理,目的是减小环境因素的影响,消除无用的噪点。然后提取目标图像特征,判断路面裂缝类型,标记出路面裂缝的位置。最后通过设计的 GUI 界面来集成各个关键步骤,显示所得到的试验结果。仿真结果表明,设计的路面裂缝检测识别系统是高效可行的。

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 1216

定位分析RCU stall问题

在编译内核时打开或者在启动参数中增加这样统计信息是在采样周期中收集的。其中number行分别是发生stalled的cpu上的硬中断,软中断,和上下文的切换次数cputime行的前三列分别是硬中断,软中断和任务消耗的cpu时间,单位为ms.最后一个数字是统计的周期时间(ms).因为用户态的任务通常不会引起RCU CPU stalls,所以任务通常是内核任务,这也是为什么只统计system cpu的原因.

happyAnger6的专栏 1994

esp8266和esp32区别是什么

能力、安全性和扩展性。然而,ESP8266由于其成本低廉和易于使用,仍然在许多简单的应用中占有一席之地。选择两者之间的哪一个,取决于项目的具体需求和预算。总结来说,ESP32在许多方面都优于ESP8266,包括处理能力、内存、

Leo6654的博客 2666

ABAP Cloud:面向云时代的 ABAP 开发范式与实践指南

摘要: 本文系统讲解SAP ABAP Cloud开发模型,涵盖其架构、工具链、规范及实践。ABAP Cloud作为统一开发模型,支持在SAP BTP和S/4HANA上构建云就绪应用,遵循clean core原则,通过RAP(RESTful应用编程模型)实现低耦合扩展。文章详细解析其核心构成(RAP、稳定API、clean core约束)、工具链(ADT、软件物流)及两种典型扩展场景(on-stack与side-by-side),并附可运行RAP示例代码(如销售笔记应用)。重点强调通过规范开发确保系统可升级性

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 399

新的外设如果使用

 对于多字节的高字节在前低字节在后(如 2bytes 的 00 06 表示 0006,而不是 0600) 设备地址在没有生成之前为缺省的 0xffffffff,一旦主控通过指令生成了设备地址,则所。 校验和是从包标识至校验和之间所有字节之和,包含包标识不包含校验和,超出 2 字节的进。 包长度 = 包长度至校验和(指令、参数或数据)的总字节数,包含校验和,但不包含包长。位置:examples/peripherals/uart/uart_echo。有的数据包都必须按照生成的地址收发。

嵌入式心学修行者,以代码为器,以良知为核。探索嵌入式开发中的“知行合一”,在编译、调试、部署中事上磨练致心中之光,铸可靠之器。 409

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

esp32外设使用-MCPWM基本使用方法

有刷/无刷电机驱动开关电源DAC脉冲宽度计算,测距/测速FOC驱动。

啸风 9257

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

ESP32开发--外设之GPIO的使用

ESP32基础系列 ESP32初体验之环境搭建 ESP32外设学习之GPIO ESP32外设学习之UART ESP32开发之给模块连上网络 ESP32的一键配网与Airkiss配网 ESP32使用NVS存储数据 ESP32一键配网+自动重连 ESP32之Cmake的介绍与组件的添加 ESP32之为配网模式添加按键和指示灯 本文目录 一.ESP32的GPIO简介 二.官方例程解析 一.ESP32的GPIO简介 要知道如何控制芯片的GPIO,必须由软件和硬件结合,也就是我们需要...

啤酒的博客 3909

使用Arduino开发ESP32(11):IO口与相关外设说明与记录

文章目录目的总结 目的 总结

Naisu的各种笔记 2万+

esp32 使用rmt外设驱动舵机

利用esp32 的rmt发送通道产生pwm来驱动舵机,使得一些支持rmt的芯片拥有更多的pwm输出,可以驱动更多舵机

qq_26700087的博客 1166

ESP32使用外设RMT控制WS2812灯条

ESP32使用外设RMT控制WS2812,官方的库、例程以及使用方法,WS2812颜色渐变程序

tian_milk的博客 1万+

ESP32 基本外设

ESP32-WROOM-32模块搭载的核心芯片型号为 ‌ESP32-D0WDQ6内置 ‌512KB SRAM‌,外扩 ‌4MB SPI Flash‌boot loader一般在0x1000开始的位置。Flash的话,0x9000到0xD000是数据区,0xD000到0xF000是ota数据区,0xF000到0x10000是phy数据区,0x10000往后是工厂程序区和其他分区。

qq_45578181的博客 1697
上一篇: 【树莓派学习】开发环境配置
下一篇: ESP32学习笔记_FreeRTOS(1)——Task的创建和使用
flashier
博客等级 码龄3年 381粉丝 79原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值