1.Beetle ESP32-C3简介
Beetle ESP32-C3是一款基于ESP32-C3 RISC-V 32位单核处理器芯片的主控板,专为物联网 (IoT) 设备而设计。
Beetle ESP32-C3在仅硬币大小的体积上引出了多达13个IO口,制作项目时不必再担心IO口不够用的情况,同时主控板还集成锂电池充电管理功能,可以直接连接锂电池,不需要外围模块,同时保证应用体积和安全性。
Beetle ESP32-C3配套的扩展板在未增加太大体积的情况下,引出了更多的电源,在制作项目时焊接更加方便,板载的GDI显示屏接口解决使用屏幕时的接线烦恼。
Beetle ESP32-C3支持Wi-Fi 和 Bluetooth 5 (LE) 双模通讯,降低了设备配网难度,同时还支持蓝牙 Mesh (Bluetooth Mesh) 协议和乐鑫 Wi-Fi Mesh可以实现更高的通讯稳定性和更大的覆盖面积,适用于广泛的物联网应用场景。
Beetle ESP32-C3拥有详细的示例教程,通过教程可以轻松的使用控制器的无线功能,连接阿里云、IFTTT等物联网平台,同时DFRobot商城提供了上千种传感器和执行器,这些传感器和执行器也有详细的教程,因此你可以轻松搭建出自己的物联网系统。
Beetle ESP32-C3可以使用Arduino IDE、ESP-IDF、MicroPython进行编程,C语言、python都可以轻松的操纵硬件。
V2.0.0改动:增加BOOT(IO9)和RST按键,可以更方便的进入下载启动模式和复位。
2.产品特性
- 超小体积,尺寸仅25*20.5mm
- 板载锂电池充电管理,充放电更安全
- 配套扩展板,制作项目、使用屏幕更加方便
- RISC-V 32位内核
- 支持Wi-Fi 和 Bluetooth 5 (LE) 双模通讯
3.产品参数
基本参数
- 工作电压: 3.3V
- Type-C输入电压: 5V DC
- VIN输入电压:5V DC
- 工作电流: 25mA
- deep sleep
- <15uA (电池供电)
- <600uA (VIN 5V供电)
- 最大充电电流: 400mA
- 工作温度:-40~105℃
- 模块尺寸:25x20.5mm
硬件信息
- 处理器:32 位 RISC-V 单核处理器
- 主频:160 MHz
- SRAM:400KB
- ROM:384KB
- Flash:4MB
- RTC SRAM:8KB
- 时钟:外置 (32 kHz) 晶振、内置快速 RC 振荡器时钟 17.5 MHz(可调节)、以及PLL时钟
- USB: USB 2.0 可达 12Mbit/s
WIFI
- WIFI协议:IEEE 802.11b/g/n
- WIFI频宽: 2.4 GHz 频带支持 20 MHz 和 40 MHz 频宽
- WIFI模式:Station 模式、SoftAP 模式、SoftAP+Station 模式和混杂模式
- WIFI频率:2.4GHz
- 帧聚合: TX/RX A-MPDU, TX/RX A-MSDU
蓝牙
- 蓝牙协议:Bluetooth 5、Bluetooth mesh
- 蓝牙频率:125 Kbps、500 Kbps、1 Mbps、2 Mbps
接口引脚
- 数字I/O x13
- LED PWM 控制器 6个通道
- SPI x1
- UART x2
- I2C x1
- I2S x1
- 红外收发器:发送通道 x2、接收通道 x2,(任意管脚)
- 2 × 12 位 SAR 模/数转换器, 6 个通道
- DMA 控制器,3 个接收通道和 3 个发送通道
4.功能引脚示意图
功能指示
- Type-C:5V
- LED/10:板载LED引脚
- BOOT/9: BOOT按键
- ESP32-C3模组:乐鑫公司推出的最新ESP32-C3模组
- RST:复位按键
- TP4057:TP4057锂电池充电管理芯片
- Charge:充电指示灯
- 熄灭:未接入电源或已充满
- 常亮:充电中
- 闪烁:电池未接入
- 18Pin-FPC:GDI显示屏接口
引脚示意
引脚概述
- GPIO:常规引脚
- 模拟端口:模拟输入引脚
- JTAG:调试接口
- ADC:模数转换
- VIN:5V电源输入
- BAT:电池接入口
GDI显示接口
此接口为DFRbot专用GDI显示屏接口,使用18pin-FPC线连接,单线材连接屏幕,为您提供最简捷的屏幕使用方式。
以下是GDI接口使用的引脚列表
FPC PINS | Beetle ESP32 C3 PINS | Description |
---|---|---|
VCC | 3V3 | 3.3V |
BLK(PWM调光) | 10 | 背光 |
GND | GND | GND |
SCLK | 4/SCK | SPI时钟 |
MOSI | 6/MOSI | 主机输出,从机输入 |
MISO | 5/MISO | 主机输入,从机输出 |
DC | 1 | 数据/命令 |
RES | 2 | 复位 |
CS | 7 | TFT片选 |
SDCS | 0 | SD卡片选 |
FCS | NC | 字库 |
TCS | 3 | 触摸 |
SCL | 9/SCL | I2C时钟 |
SDA | 8/SDA | I2C数据 |
INT | NC | INT |
BUSY-TE | NC | 防撕裂引脚 |
X1 | NC | 自定义引脚1 |
X2 | NC | 自定义引脚2 |
使用FPC连接屏幕时根据GDL demo配置所需对应的引脚号即可,通常只需要根据不同主控配置三个引脚
支持GDI的显示屏:
- 1.54" 240x240 IPS广视角TFT显示屏
- 1.8”128x160 IPS TFT LCD 显示屏
- 2.0" 320x240 IPS广视角TFT显示屏
- 2.8" 320x240 IPS TFT电阻触摸显示屏
- 3.5" 480x320 IPS TFT电容触摸显示屏
- 1.51”OLED 透明屏幕
5. 首次使用
5.1 Arduino环境配置
当您首次使用Beetle-ESP32-C3,您需要了解以下步骤
- 添加IDE中的json链接
- 下载主控的核心
- 选择开发板以及串口
- 打开示例程序并烧录
- 了解串口监视器
-
Arduino IDE 编译环境配置
-
配置URL网址到Arduino IDE
-
打开Arduino IDE,点击File->Preferences,如下图所示:
- 在新打开的界面中,点击如下图红色圆圈中的按钮
- 将如下链接地址复制到新弹出的对话框中:https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
- 注意:如果您之前安装过其它的环境,那么您可以在上一个链接地址开头或末尾处直接回车并将上面链接粘贴在其上下任意一行
- 点击OK
- 更新板卡
- 打开Tools->Board:->Boards Manager...,如下图所示:
- Boards Manager会自动更新板卡,如下图所示:
- 更新完成后,您可以在上方输入esp32,出现如下情况时选择esp32点击安装即可(当前安装为2.0.0版本):
- 等待如下进度条结束:
- 安装完成后,列表会显示已经安装esp32主板,如下图所示:
- 点击Tools->Board:,选择DFRobot Beetle ESP32-C3
- 在开始前您还需要配置如下设置(当您选择 Disabled 时,串口为RX(20)、TX(21),如果您需要通过USB在Arduino监视器上打印,您需要选择Enable)
- 点击Port选择对应的串口(如果串口不停的出现,然后消失,请将引脚9连接GND,重新上电)
5.2 LED闪烁
板载LED灯默认为引脚10
代码
int led = 10;
void setup() {
pinMode(led,OUTPUT);
}
void loop() {
digitalWrite(led,HIGH);
delay(1000);
digitalWrite(led,LOW);
delay(1000);
}
- 将以上程序粘贴到程序框中
- 点击箭头等待程序编译并烧录至开发板
烧录成功
- 如图所示及烧录成功(如果不能烧录,请将引脚9连接GND,重新上电,再烧录)
- 您会看见板载LED灯开始闪烁
6. Beetle-ESP32C3基础教程(此处介绍为Arduino之间的差别处)
6.1 PWM输出
ESP32C3的PWM功能需要提前定义声明
-
ledcSetup(Channel, freq, resolution)
说明:设置PWM通道参数
参数:- LedChannel: 产生PWM信号的通道
- freq: PWM频率
- resolution: PWM分辨率
-
ledcAttachPin(GPIO, Channel)
说明:将PWM通道绑定到GPIO
参数:- GPIO: 输出PWM信号的GPIO
- Channel: 产生PWM信号的通道
-
ledcWrite(Channel, dutyCycle)
说明:输出PWM信号
参数:- Channel: 产生PWM信号的通道
- dutyCycle: PWM值
示例
ESPC3的PWM由于其可以自由映射到其它端口进行输出,您需要进行几步设置,该案例将使用帮助您理解其中的步骤
该案例你可以看到LED灯逐渐变亮变暗
/*
* LED呼吸灯示例
*/
const int ledPin = 10; // PWM生成后实际输出引脚
//设置PWM参数
const int freq = 5000;//PWM频率
const int ledChannel = 0;//信号生成GPIO
const int resolution = 8;//8位分辨率
void setup(){
//PWM参数设置
ledcSetup(ledChannel, freq, resolution);
//将生成信号通道绑定到输出通道上
ledcAttachPin(ledPin, ledChannel);
}
void loop(){
//逐渐变亮
for(int dutyCycle = 0; dutyCycle <= 255; dutyCycle++){
// changing the LED brightness with PWM
ledcWrite(ledChannel, dutyCycle);
delay(15);
}
//逐渐变暗
for(int dutyCycle = 255; dutyCycle >= 0; dutyCycle--){
// changing the LED brightness with PWM
ledcWrite(ledChannel, dutyCycle);
delay(15);
}
}
6.2 中断
ESP32C3中断可自由分配
-
pinMode(GPIO,INPUT_PULLUP);
说明:外部中断引脚定义
参数: -
GPIO:ESP32C3想要作为中断的引脚号。
-
INPUT_PULLINGUP:设置为上拉模式。
-
attachInterrupt(digitalPinToInterrupt(pin), ISR, mode)
说明:外部中断
参数: -
pin:Arduino引脚号。
-
ISR:发生中断时要调用的ISR;此函数必须不带任何参数,并且不返回任何内容。有时将此功能称为中断服务程序。
-
mode:定义何时触发中断。预定义了三个常数作为有效值:
-
CHANGE 当引脚电平发生变化时触发中断引脚
-
RISING 当引脚电平由低电平变为高电平时触发中断引脚
-
FALLING 当引脚电平由高电平变为低电平时触发中断引脚
-
detachInterrupt(digitalPinToInterrupt(pin))
说明:关闭给定的中断。
参数:- pin:需要禁用的中断引脚
-
interrupts()
说明:重新启用中断(在被noInterrupts()禁用之后。中断允许某些重要任务在后台发生并默认启用。当禁用中断时,某些功能将不起作用,并且传入通信可能会被忽略。可能会稍微破坏代码的时序,但是对于特别关键的代码部分可能会被禁用。 -
noInterrupts()
说明:禁用中断(您可以使用来重新启用它们interrupts())。中断允许某些重要任务在后台发生,并且默认情况下启用。禁用中断时,某些功能将不起作用,并且传入的通信可能会被忽略。但是,中断可能会稍微破坏代码的时序,并且可能会在代码的特别关键的部分将其禁用。
6.3 串口
ESP32C3串口初始化需要映射
- Serial1.begin(baud, config, rxPin, txPin);
说明:Serial1初始化
参数: - baud:波特率。
- config:数据位和停止位设置。
- rxPin:RX引脚
- txPin:TX引脚
Serial1.begin(9600,SERIAL_8N1,/*rx =*/0,/*Tx =*/1);
6.4 舵机
ESP32-C3不能通过Servo库驱动舵机,需要在项目->加载库->管理库中搜索安装ESP32_ISR_Servo库驱动舵机
7.进阶教程
7.1 使用SD库
SD类
-
begin(cspin)
说明:初始化SD卡库和 SD卡。当使用SD.begin()时﹐默认将Arduino SPI的SS引脚连接到SD卡的CS使能选择端;也可以使用begin(cspin)指定一个引脚连接到SD卡的CS使能选择端,但仍需保证 SPI 的SS引脚为输出模式,否则SD卡库将无法运行。
参数:cspin:连接到SD 卡CS端的Arduino引脚。
返回值: boolean型值,为 true表示初始化成功;为 false表示初始化失败。 -
exists()
说明:检查文件或文件夹是否存在于SD卡中。语法:SD. exists( filename)
参数:
filename:需要检测的文件名。其中可以包含路径,路径用“/”分隔。
返回值: boolean型值,为 true表示文件或文件夹存在;为false表示文件或文件夹不存在。 -
open()
说明:打开SD卡上的一个文件。如果文件不存在,且以写入方式打开,则Arduino会创建一个指定文件名的文件。(所在路径必须事先存在)
语法:SD.open( filename)SD.open(filename,mode)
参数:
filename:需要打开的文件名。其中可以包含路径,路径用“/”分隔。
mode(可选):打开文件的方式,默认使用只读方式打开。也可以使用以下两种方式打开文件:
FILE_READ:只读方式打开文件;FILE_WRITE,写入方式打开文件。
返回值:返回被打开文件对应的对象﹔如果文件不能打开,则返回false。
FILE_WRITE,写入方式打开文件。
返回值:返回被打开文件对应的对象;如果文件不能打开,则返回false。 -
remove()
说明:从SD卡移除一个文件。如果文件不存在,则函数返回值是不确定的,因此在移除文件之前,最好使用SD. exists(filename)先检测文件是否存在。
语法:SD. remove( filename)
参数:
filename,需要移除的文件名。其中可以包含路径,路径用“!”分隔。
返回值: boolean型值,为true表示文件移除成功;为false表示文件移除失败。 -
mkdir(filename)
说明:创建文件夹。
参数:- filename,需要创建的文件夹名。其中可以包含路径,路径用“/”分隔。返回值: boolean型值,为 true表示创建成功;为false表示创建失败。
-
rmdir(filename)
说明:移除文件夹。被移除的文件夹必须是空的。语法:SD.rmdir( filename)
参数:- filename,需要移除的文件夹名。其中可以包含路径,路径用“/”分隔。
返回值 : boolean型值,为 true表示移除成功;为false表示移除失败。
- filename,需要移除的文件夹名。其中可以包含路径,路径用“/”分隔。
File类
File类提供了读/写文件的功能,该类的功能与之前使用的串口相关函数的功能非常类似。其成员函数如下。
-
available()
说明:检查当前文件中可读数据的字节数。语法:file. available()
参数:- file:一个 File类型的对象。返回值:可用字节数。
-
close()
说明:关闭文件,并确保数据已经被完全写入SD卡中。语法:file. close()
参数:- file:一个 File类型的对象。返回值:无。
-
flush()
说明:确保数据已经写入SD卡。当文件被关闭时,flush()会自动运行。语法: file.flush
参数:- file:一个File类型的对象。返回值:无。
-
peek()
说明:读取当前所在字节,但并不移动到下一个字节。
参数- file:一个 File类型的对象。
返回值:下一个字节或者下一个字符。如果没有可读数据,则返回一1。
- file:一个 File类型的对象。
-
position( )
说明:获取当前在文件中的位置(即下一个被读/写的字节的位置)。语法:file. position()
参数:- file:一个 File类型的对象。返回值:在当前文件中的位置。
-
print()
说明:输出数据到文件。要写入的文件应该已经被打开,且等待写入。语法:file. print(data)file. print(data,BASE)
参数:- file:一个 File类型的对象。
- data,要写入的数据(可以是类型char, byte ,int .long 或 String)。
- BASE(可选),指定数据的输出形式:BIN(二进制);oCT(八进制);DEC(十进制);HEX(十六进制)。
返回值;发送的字节数。
-
println()
说明:输出数据到文件,并回车换行。语法:file. println(data)file,println(data,BASE)
参数:- file:一个 File类型的对象。
- data:要写入的数据(类型可以是char , byte , int , long或String)。
- BASE(可选),指定数据的输出形式:BIN(二进制>;OCT(八进制);DECK十进制;HEX(十六进制)返回值;发送的宇节数。
-
seek()
说明;跳转到指定位置。该位置必须在·到该文件大小之间。语法:file. seek( pos)
参数:- file:一个 File类型的对象。pos,需要查找的位置。
返回值: boolean型值,为 true表示跳转成功;为false表示跳转失败。
- file:一个 File类型的对象。pos,需要查找的位置。
-
size()
说明:获取文件的大小。语法:filue. size()
参数:- file:一个File类型的对象。
返回值:以字节为单位的文件大小。
- file:一个File类型的对象。
-
read()
说明:读取1B数据。语法:file.read参数:- file:一个 File类型的对象。
返回值:下一个字节或者字符;如果没有可读数据,则返回一1。
- file:一个 File类型的对象。
-
write()
说明;写入数据到文件。语法:file. write(data)file. write(buf,len)
参数:- file:一个File类型的对象。
- data:要写入的数据,类型可以是 byte ,char或字符串(char * ) 。buf,一个字符数组或者宁节数据。
- len:buf数组的元素个数。
返回值:发送的字节数。
-
isDirectory()
说明:判断当前文件是否为目录。语法:file.isDirectory()
参数:- file:一个File类型的对象。
返回值: boolcan型值;为 true表示是目录;为 false表示不是目录。
- file:一个File类型的对象。
-
openNextFile()
说明:打开下一个文件。语法:file.openNextFile()
参数: -
file:一个 File类型的对象。
返回值:下一个文件对应的对象。 -
rewindDirectory()
说明:回到当前目录中的第一个文件。语法:file.rewindDirectory()
参数:
file,一个File类型的对象。返回值:无。
7.2 ESP32-C3蓝牙收发
7.2.1 ESP32-C3与手机蓝牙通信
使用该实例演示ESP32-C3与手机之间的数据传输,如果需要修改或使用数据,只需更改数据接收部分或数据发送部分代码
/*
Video: https://www.youtube.com/watch?v=oCMOYS71NIU
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp
Ported to Arduino ESP32 by Evandro Copercini
Create a BLE server that, once we receive a connection, will send periodic notifications.
The service advertises itself as: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E
Has a characteristic of: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E - used for receiving data with "WRITE"
Has a characteristic of: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E - used to send data with "NOTIFY"
The design of creating the BLE server is:
1. Create a BLE Server
2. Create a BLE Service
3. Create a BLE Characteristic on the Service
4. Create a BLE Descriptor on the characteristic
5. Start the service.
6. Start advertising.
*/
/* 该示例演示了蓝牙数据透传,烧录代码,打开串口监视器,打开手机的BLE调试助手
* 1.即可看见ESP32-C3发送的数据--见APP使用图
* 2.通过BLE调试助手的输入框可向ESP32-C3发送数据--见APP使用图
* 该示例由BLE_uart示例更改而来
*/
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
BLEServer *pServer = NULL;
BLECharacteristic * pTxCharacteristic;
bool deviceConnected = false;
uint8_t txValue = 0;
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
//蓝牙连接/断开处理。当有连接/断开事件发生时自动触发
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) { //当蓝牙连接时会执行该函数
Serial.println("蓝牙已连接");
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer) { //当蓝牙断开连接时会执行该函数
Serial.println("蓝牙已断开");
deviceConnected = false;
delay(500); // give the bluetooth stack the chance to get things ready
pServer->startAdvertising(); // restart advertising
}
};
/****************数据接收部分*************/
/****************************************/
//蓝牙接收数据处理。当收到数据时自动触发
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
String rxValue = pCharacteristic->getValue();//接收数据,并赋给rxValue
//if(rxValue == "ON"){Serial.println("开灯");} //判断接收的字符是否为"ON"
if (rxValue.length() > 0) {
Serial.println("*********");
Serial.print("Received Value: ");
for (int i = 0; i < rxValue.length(); i++){
Serial.print(rxValue[i]);
}
Serial.println();
Serial.println("*********");
}
}
};
/***************************************/
/****************************************/
void setup() {
Serial.begin(115200);
BLEBegin(); //初始化蓝牙
}
void loop() {
/****************数据发送部分*************/
/****************************************/
if (deviceConnected) { //如果有蓝牙连接,就发送数据
pTxCharacteristic->setValue("Hello"); //发送字符串
pTxCharacteristic->notify();
delay(10); // bluetooth stack will go into congestion, if too many packets are sent
pTxCharacteristic->setValue("DFRobot"); //发送字符串
pTxCharacteristic->notify();
delay(10); // bluetooth stack will go into congestion, if too many packets are sent
}
/****************************************/
/****************************************/
}
void BLEBegin(){
// Create the BLE Device
BLEDevice::init(/*BLE名称*/"UART Service");
// Create the BLE Server
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic
pTxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_TX,
BLECharacteristic::PROPERTY_NOTIFY
);
pTxCharacteristic->addDescriptor(new BLE2902());
BLECharacteristic * pRxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_RX,
BLECharacteristic::PROPERTY_WRITE
);
pRxCharacteristic->setCallbacks(new MyCallbacks());
// Start the service
pService->start();
// Start advertising
pServer->getAdvertising()->start();
Serial.println("Waiting a client connection to notify...");
}
7.2.2 两个ESP32C3蓝牙通讯
使用该实例演示ESP32-C3与ESP32-C3之间的数据传输,如果需要修改或使用数据,只需更改数据接收部分或数据发送部分代码
主机代码
/**
* A BLE client example that is rich in capabilities.
* There is a lot new capabilities implemented.
* author unknown
* updated by chegewara
*/
#include "BLEDevice.h"
//#include "BLEScan.h"
// The remote service we wish to connect to.
static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b");
// The characteristic of the remote service we are interested in.
static BLEUUID charTXUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8");
static BLEUUID charRXUUID("beb5483f-36e1-4688-b7f5-ea07361b26a8");
static boolean doConnect = false;
static boolean connected = false;
static boolean doScan = false;
static BLERemoteCharacteristic* pTXRemoteCharacteristic;
static BLERemoteCharacteristic* pRXRemoteCharacteristic;
static BLEAdvertisedDevice* myDevice;
/****************数据接收部分*************/
/****************************************/
//蓝牙接收数据处理,当收到数据时自动触发
static void notifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) { //传入uint8_t* pData用于存放数据
String BLEData = "";
for(int i = 0; i < length; i++) //
BLEData += (char)pData[i];
Serial.println("*********");
Serial.print("Received Value: ");
Serial.println(BLEData);
Serial.println("*********");
//if(BLEData == "ON"){Serial.println("开灯");} //判断接收的字符是否为"ON"
//Serial.print("Notify callback for characteristic ");
//Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
//Serial.print(" of data length ");
//Serial.println(length);
}
/****************************************/
/****************************************/
//蓝牙连接/断开处理。当有连接/断开事件发生时自动触发
class MyClientCallback : public BLEClientCallbacks {
void onConnect(BLEClient* pclient) {
}
void onDisconnect(BLEClient* pclient) {
connected = false;
Serial.println("onDisconnect");
}
};
/**
* Scan for BLE servers and find the first one that advertises the service we are looking for.
*/
//蓝牙扫描处理事件。当开启扫描时自动触发
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
/**
* Called for each advertising BLE server.
*/
void onResult(BLEAdvertisedDevice advertisedDevice) {
//Serial.print("BLE Advertised Device found: ");
//Serial.println(advertisedDevice.toString().c_str());
// We have found a device, let us now see if it contains the service we are looking for.
if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
BLEDevice::getScan()->stop();
myDevice = new BLEAdvertisedDevice(advertisedDevice);
doConnect = true;
doScan = true;
} // Found our server
} // onResult
}; // MyAdvertisedDeviceCallbacks
void setup() {
Serial.begin(115200);
Serial.println("Starting Arduino BLE Client application...");
bleBegin();
}
void loop() {
// If the flag "doConnect" is true then we have scanned for and found the desired
// BLE Server with which we wish to connect. Now we connect to it. Once we are
// connected we set the connected flag to be true.
if (doConnect == true) {
if (connectToServer()) {
Serial.println("We are now connected to the BLE Server.");
} else {
Serial.println("We have failed to connect to the server; there is nothin more we will do.");
}
doConnect = false;
}
/****************数据发送部分*************/
/****************************************/
if (connected) { //当连接到蓝牙从机时发送数据
pTXRemoteCharacteristic->writeValue("我是主机");
pTXRemoteCharacteristic->writeValue("Hello client");
}
if(!connected){ //当没有连接到蓝牙从机时重新扫描
BLEDevice::getScan()->start(5,false); // this is just example to start scan after disconnect, most likely there is better way to do it in arduino
}
/****************************************/
/****************************************/
delay(1000);
}
void bleBegin()
{
BLEDevice::init("");
// Retrieve a Scanner and set the callback we want to use to be informed when we
// have detected a new device. Specify that we want active scanning and start the
// scan to run for 5 seconds.
BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());//扫描处理函数
pBLEScan->setInterval(1349);//设置扫描间隔时间
pBLEScan->setWindow(449);//主动扫描时间
pBLEScan->setActiveScan(true);
pBLEScan->start(5, false);//扫描时间,单位秒
}
//蓝牙连接处理
bool connectToServer() {
Serial.print("Forming a connection to ");
Serial.println(myDevice->getAddress().toString().c_str());
BLEClient* pClient = BLEDevice::createClient();
Serial.println(" - Created client");
pClient->setClientCallbacks(new MyClientCallback());
// Connect to the remove BLE Server.
pClient->connect(myDevice); // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)
Serial.println(" - Connected to server");
pClient->setMTU(517); //set client to request maximum MTU from server (default is 23 otherwise)
// Obtain a reference to the service we are after in the remote BLE server.
BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
if (pRemoteService == nullptr) {
Serial.print("Failed to find our service UUID: ");
Serial.println(serviceUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found our service");
// Obtain a reference to the characteristic in the service of the remote BLE server.
pTXRemoteCharacteristic = pRemoteService->getCharacteristic(charTXUUID);
if (pTXRemoteCharacteristic == nullptr) {
Serial.print("Failed to find our characteristic UUID: ");
Serial.println(charTXUUID.toString().c_str());
pClient->disconnect();
return false;
}
pRXRemoteCharacteristic = pRemoteService->getCharacteristic(charRXUUID);
if (pRXRemoteCharacteristic == nullptr) {
Serial.print("Failed to find our characteristic UUID: ");
Serial.println(charRXUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found our characteristic");
if(pRXRemoteCharacteristic->canNotify())
pRXRemoteCharacteristic->registerForNotify(notifyCallback);
connected = true;
return true;
}
从机代码
/*
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp
Ported to Arduino ESP32 by Evandro Copercini
updates by chegewara
*/
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLE2902.h>
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID_RX "beb5483e-36e1-4688-b7f5-ea07361b26a8"
#define CHARACTERISTIC_UUID_TX "beb5483f-36e1-4688-b7f5-ea07361b26a8"
uint8_t txValue = 0;
bool deviceConnected = false;
BLECharacteristic *pTxCharacteristic;
//蓝牙连接/断开处理。当有连接/断开事件发生时自动触发
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) { //当蓝牙连接时会执行该函数
Serial.println("蓝牙已连接");
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer) { //当蓝牙断开连接时会执行该函数
Serial.println("蓝牙已断开");
deviceConnected = false;
delay(500); // give the bluetooth stack the chance to get things ready
BLEDevice::startAdvertising(); // restart advertising
}
};
/****************数据接收部分*************/
/****************************************/
//蓝牙接收数据处理。当收到数据时自动触发
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
String rxValue = pCharacteristic->getValue(); //使用rxValue接收数据
//if(rxValue == "ON"){Serial.println("开灯");} //判断接收的字符是否为"ON"
if (rxValue.length() > 0) {
Serial.println("*********");
Serial.print("Received Value: ");
for (int i = 0; i < rxValue.length(); i++)
Serial.print(rxValue[i]); //将接收的数据打印出来
Serial.println();
Serial.println("*********");
}
}
};
/****************************************/
/****************************************/
void setup() {
Serial.begin(115200);
Serial.println("Starting BLE work!");
bleBegin();
}
/****************数据发送部分*************/
/****************************************/
void loop() {
if(deviceConnected){ //当有设备连接时发送数据
pTxCharacteristic->setValue("我是从机");
pTxCharacteristic->notify();
pTxCharacteristic->setValue("Hello Sever");
pTxCharacteristic->notify();
}
/****************************************/
/****************************************/
delay(1000);
}
void bleBegin()
{
BLEDevice::init(/*BLE名称*/"Long name works now");
BLEServer *pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
BLEService *pService = pServer->createService(SERVICE_UUID);
BLECharacteristic *pRxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_RX,
BLECharacteristic::PROPERTY_WRITE
);
pRxCharacteristic->setCallbacks(new MyCallbacks());
pTxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_TX,
BLECharacteristic::PROPERTY_NOTIFY
);
pTxCharacteristic->addDescriptor(new BLE2902());
pService->start();
// BLEAdvertising *pAdvertising = pServer->getAdvertising(); // this still is working for backward compatibility
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
pAdvertising->setMinPreferred(0x12);
BLEDevice::startAdvertising();
}
7.3 WIFI控制LED
ESP32C3具有WIFI功能,以下示例使用ESP32C3创建了一个wifi服务器,使用客户端连接到该服务器,控制LED的亮灭
步骤
1.连接到WIFI”Beetle ESP32 C3“,已设置WIFI密码:12345678
2.访问网址 http://192.168.4.1/ON 来打开灯 访问 http://192.168.4.1/OFF 来关闭灯
3.在访问后通过点击上下 here 来便捷控制灯的亮灭而不需要输入网址进行
代码
/*
步骤:
1.连接到WIFI”Beetle ESP32 C3“,已设置WIFI密码:12345678
2.访问网址 http://192.168.4.1/ON 来打开灯 访问 http://192.168.4.1/OFF 来关闭灯
3.在访问后通过点击上下 here 来便捷控制灯的亮灭而不需要输入网址进行
*/
#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiAP.h>
#define myLED 10 //设置引脚10为LED引脚
// 设置WIFI名称以及密码
const char *ssid = "Beetle ESP32 C3";//WIFI名称
const char *password = "12345678";//密码
WiFiServer server(80);//网页服务端口默认为80
void setup() {
pinMode(myLED, OUTPUT);
Serial.begin(115200);
Serial.println();
Serial.println("Configuring access point...");
//如果想要无密码开放网络请删除password
WiFi.softAP(ssid, password);
IPAddress myIP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(myIP);
server.begin();
Serial.println("Server started");
}
void loop() {
WiFiClient client = server.available(); // 检测等待连接
if (client) { // 检测是否连接
Serial.println("New Client.");
String currentLine = ""; // 创建String变量来保存数据
while (client.connected()) { // 保持连接时一直循环
if (client.available()) { // 检测连接是否有数据
char c = client.read(); // 读取接收的数据
//Serial.write(c); // 打印在串行监视器
if (c == '\n') { // 如果读取的是换行符
//结尾用换行符提醒结束
if (currentLine.length() == 0) {
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println();
//将字符与here连接
client.print("Click <a href=\"/ON\">here</a> to turn ON the LED.<br>");
client.print("Click <a href=\"/OFF\">here</a> to turn OFF the LED.<br>");
// HTTP响应为空行
client.println();
// 跳出循环
break;
} else { // 如果有一个换行符就清除变量缓存的数据
currentLine = "";
}
} else if (c != '\r') { // 如果获得回车以外的字符
currentLine += c; // 获得的字符添加到变量末尾
}
// 检查是否获得/ON或者/OFF
if (currentLine.endsWith("/ON")) {
digitalWrite(myLED, HIGH); //得到/ON时打开灯
}
if (currentLine.endsWith("/OFF")) {
digitalWrite(myLED, LOW); //得到/OFF时关闭灯
}
}
}
// 关闭连接
client.stop();
Serial.println("Client Disconnected.");
}
}
结果
使用手机连接该wifi,通过浏览器访问192.168.4.1,如图所示显示ip地址为192.168.4.1,服务已开启
使用浏览器访问该ip地址得到如下图所示
尝试分别点击链接控制LED吧
成员函数
-
WiFiServer server()
说明:设置服务器端口 -
softAP(ssid,password)
说明:将WiFi配置为AP模式,并设置名称以及密码
参数:- ssid: AP模式的wifi名字
- password: ap模式的wifi密码
-
server.available()
说明:检测服务端口是否有连接(WIFI是否连接) -
client.connected()
说明:检测连接状态
返回值:true/false -
client.available()
说明:检测连接WIFI是否有数据输入 -
client.read()
说明:读取WIFI接收数据 -
currentLine.endsWith()
说明:检测是否获得括号内容 -
client.stop()
说明:断开连接
7.4 ESP-NOW数据传输
ESP-NOW是乐鑫开发的终端数据传输、无连接的快速通讯技术,适用于智能灯、遥控控制、传感器数据回传等场景。
7.4.1 获取控制器MAC地址
烧录该代码,打开串口即可看到设备MAC地址
#include "WiFi.h"
void setup(){
Serial.begin(115200);
WiFi.mode(WIFI_MODE_STA);
}
void loop(){
Serial.println(WiFi.macAddress());
delay(1000);
}
7.4.2 收发数据
填写MAC地址后烧录代码,可实现两个设备间相互收发数据
#include <esp_now.h>
#include <WiFi.h>
//MAC
uint8_t MAC1[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
typedef struct struct_message {
char a[16];
int b;
float c;
bool d;
} struct_message;
struct_message sendData;
struct_message recvData;
esp_now_peer_info_t peerInfo;
//发送回调函数
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
if(status == ESP_NOW_SEND_SUCCESS){
Serial.println("Send_Success");
}else{
Serial.println("Send_Fail");
}
}
//接收回调函数
void OnDataRecv(const uint8_t * mac, const uint8_t *Data, int len) {
memcpy(&recvData, Data, sizeof(recvData));
Serial.print("Bytes received: ");
Serial.println(len);
Serial.println(recvData.a);
Serial.println(recvData.b);
Serial.println(recvData.c);
Serial.println(recvData.d);
Serial.println("---------");
}
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
//Init ESP-NOW
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing");
return;
}
//注册发送回调函数
esp_now_register_send_cb(OnDataSent);
peerInfo.channel = 0;
peerInfo.encrypt = false;
//注册MAC1设备
memcpy(peerInfo.peer_addr, MAC1, 6);
if (esp_now_add_peer(&peerInfo) != ESP_OK){
Serial.println("Failed to add peer");
return;
}
//注册接收回调函数
esp_now_register_recv_cb(OnDataRecv);
}
void loop() {
strcpy(sendData.a, "DFRobot");
sendData.b = 10;
sendData.c = 9.9;
sendData.d = true;
esp_err_t result = esp_now_send(MAC1, (uint8_t *)&sendData, sizeof(sendData));
if (result == ESP_OK) {
Serial.println("Send success");
}
else {
Serial.println("Send Fail");
}
delay(2000);
}
7.4.3 给多个设备发送数据
填写MAC地址后烧录代码,可同时给多个设备发送数据
#include <esp_now.h>
#include <WiFi.h>
//MAC
uint8_t MAC1[] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};
uint8_t MAC2[] = {0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB};
typedef struct struct_message {
uint8_t ID;
int data;
} struct_message;
struct_message sendData;
struct_message recvData;
esp_now_peer_info_t peerInfo;
// Callback when data is sent 发送回调函数
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
if(status == ESP_NOW_SEND_SUCCESS){
Serial.println("Send Success");
}else{
Serial.println("Send Fail");
}
}
// Callback when data is received 接收回调函数
void OnDataRecv(const uint8_t * mac, const uint8_t *Data, int len) {
memcpy(&recvData, Data, sizeof(recvData));
Serial.print("Bytes received: ");
Serial.println(len);
Serial.println(recvData.ID);
Serial.println(recvData.data);
Serial.println("---------");
}
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
//Init ESP-NOW
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing");
return;
}
//注册发送回调函数
esp_now_register_send_cb(OnDataSent);
peerInfo.channel = 0;
peerInfo.encrypt = false;
//注册MAC1设备
memcpy(peerInfo.peer_addr, MAC1, 6);
if (esp_now_add_peer(&peerInfo) != ESP_OK){
Serial.println("Failed to add peer");
return;
}
//注册MAC2设备
memcpy(peerInfo.peer_addr, MAC2, 6);
if (esp_now_add_peer(&peerInfo) != ESP_OK){
Serial.println("Failed to add peer");
return;
}
//注册接收回调函数
esp_now_register_recv_cb(OnDataRecv);
}
void loop() {
sendData.ID = 0;
sendData.data = 10;
//向所有注册设备发送信息
esp_err_t result = esp_now_send(0, (uint8_t *)&sendData, sizeof(sendData));
if (result == ESP_OK) {
Serial.println("Send success");
}
else {
Serial.println("Send Fail");
}
//向指定MAC设备发送信息
//esp_err_t result = esp_now_send(MAC1, (uint8_t *)&sendData, sizeof(sendData));
//if (result == ESP_OK) {
// Serial.println("Send success");
//}
//else {
// Serial.println("Send Fail");
//}
delay(2000);
}
8.应用示例拓展
8.1 OLED显示测量温湿度
获取温湿度显示在OLED屏幕上是非常直观有趣的项目,下面你将学会基础的OLED显示以及使用I2C接口获取温湿度传感器数据。
您还需准备
- 0.96”128x64 IIC/SPI OLED单色显示屏 x1
- 母母头杜邦线 若干
- FermionSHT30温湿度传感器 x1 (或使用相同SHT3x代码库其它SHT温湿度传感器)
1.您需要先安装SHT3x库
关于如何安装库点击这里
2.接线图
按照上图连接好线拷贝下面代码,您将看见你周围的温湿度状况。
代码
#include <Arduino.h>
#include <U8g2lib.h> //导入字库
//#include <SPI.h>
#include <Wire.h>
#include <DFRobot_SHT3x.h>
/*
---显示屏硬件I2C接口---
U8G2_R0 不旋转,横向,绘制方向从左到右
U8G2_R1 顺时针旋转90度,绘制方向从上到下
U8G2_R2 顺时针旋转180度,绘制方向从右到左
U8G2_R3 顺时针旋转270度,绘制方向从下到上
U8G2_MIRROR 正常显示镜像内容(v2.6.x版本以上使用) 注意:U8G2_MIRROR需要与setFlipMode()配搭使用.
U8x8_PIN_NONE 表示引脚为空,不会使用复位引脚
---显示屏硬件SPI接口---
cs 按引脚接上即可(引脚可自己选择)
dc 按引脚接上即可(引脚可自己选择)
*/
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(/* rotation=*/U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
//当ADR接VDD时可选择0x45当ADR接GND时可选择0x44
//默认为0x45,RST(复位脚)不用连接
DFRobot_SHT3x sht3x(&Wire,/*address=*/0x45,/*RST=*/4);
//使用SPI需要注释上方代码,使用下方代码
//DFRobot_SHT3x sht3x;
void setup() {
Serial.begin(115200);
u8g2.begin();
u8g2.setFontPosTop();//使用drawStr显示字符串时,默认标准为显示字符的左下角坐标。本函数的功能可理解为将坐标位置改为显示字符串的左上角为坐标标准。
//初始化传感器
while (sht3x.begin() != 0) {
Serial.println("Failed to Initialize the chip, please confirm the wire connection");
delay(1000);
}
Serial.print("Chip serial number");
Serial.println(sht3x.readSerialNumber());
if(!sht3x.softReset()){
Serial.println("Failed to Initialize the chip....");
}
}
void loop() {
//清理屏幕
u8g2.clearBuffer();
//将温度、湿度读取赋值用于显示
float temp = sht3x.getTemperatureC();
float humi = sht3x.getHumidityRH();
//显示温度
u8g2.setFont(u8g2_font_osb18_tf); // 选择字体以及大小(见官方)
u8g2.drawStr(5,10,"Temp");//在指定位置写出字符
u8g2.setFont(u8g2_font_t0_18b_tr);
u8g2.setCursor(75, 15);//显示从该位置开始
u8g2.print(temp);
//显示湿度
u8g2.setFont(u8g2_font_osb18_tf);
u8g2.drawStr(5,40,"Humi");
u8g2.setFont(u8g2_font_t0_18b_tr);
u8g2.setCursor(75, 45);
u8g2.print(humi);
u8g2.sendBuffer();
delay(1000);
}
结果
成员函数
-
u8g2.drawStr(x,y,"Temp")
说明:指定屏幕位置显示自定义内容
参数:- X、Y: 开始写的坐标(显示字体从左下角向右上角显示)
- "Temp": 可填英文和数字
-
u8g2.setCursor(x,y)和u8g2.print()
说明:搭配使用,前者为显示开始位置,后者同Arduino的print相同作用
参数:- X、Y: 开始写的坐标(显示字体从左下角向右上角显示)
8.2 WIFI获取温湿度
本示例参照示例8.3而来,进一步学会局域网下Wifi的信息传递,你可以学会如何访问局域网下的IP地址来获得在另一处的SHT30的温湿度传感器状态
您还需准备
- FermionSHT30温湿度传感器 x1 (或使用相同SHT3x代码库其它SHT温湿度传感器)
- 母母头杜邦线 若干
您还需要按照示例9.1中步骤安装传感器库以及将其与ESP32连接。
接线图
步骤
1.连接到WIFI”Beetle ESP32 C3“,已设置WIFI密码:12345678
2.访问网址 http://192.168.4.1/GET 来获取局域网中的温湿度信息
3.在温湿度显示网页您可以通过刷新来更新传感器的数据
代码
/*
本示例是SHT30连接上ESPC3通过局域网获取温湿度
*/
#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiAP.h>
#include <DFRobot_SHT3x.h>
//当ADR接VDD时可选择0x45当ADR接GND时可选择0x44
//默认为0x45,RST(复位脚)不用连接
DFRobot_SHT3x sht3x(&Wire,/*address=*/0x45,/*RST=*/4);
//使用SPI需要注释上方代码,使用下方代码
//DFRobot_SHT3x sht3x;
// 设置WIFI名称以及密码
const char *ssid = "Beetle ESP32 C3";//WIFI名称
const char *password = "12345678";//密码
WiFiServer server(80);//网页服务端口默认为80
// 显示上次传感器状态反馈状态
void setup() {
//pinMode(myLED, OUTPUT);
Serial.begin(115200);
Serial.println();
Serial.println("Configuring access point...");
//如果想要无密码开放网络请删除password
WiFi.softAP(ssid, password);
IPAddress myIP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(myIP);
server.begin();
Serial.println("Server started");
//初始化传感器
while (sht3x.begin() != 0) {
Serial.println("Failed to Initialize the chip, please confirm the wire connection");
delay(1000);
}
Serial.print("Chip serial number");
Serial.println(sht3x.readSerialNumber());
if(!sht3x.softReset()){
Serial.println("Failed to Initialize the chip....");
}
}
void loop() {
WiFiClient client = server.available(); // 检测等待连接
if (client) { // 检测是否连接
Serial.println("New Client.");
String currentLine = ""; // 创建String变量来保存数据
while (client.connected()) { // 保持连接时一直循环
if (client.available()) { // 检测连接是否有数据
char c = client.read(); // 读取接收的数据
//Serial.write(c); // 打印在串行监视器
if (c == '\n') { // 如果读取的是换行符
//清除掉缓存的内容
if (currentLine.length() == 0) {
client.print(" ");
break;
} else { // 如果有一个换行符就清除变量缓存的数据
currentLine = "";
}
} else if (c != '\r') { // 如果获得回车以外的字符
currentLine += c; // 获得的字符添加到变量末尾
}
// 检查是否有/GET在末尾
if (currentLine.endsWith("/GET")) {
//把温度、湿度读出
float temp = sht3x.getTemperatureC();
float humi = sht3x.getHumidityRH();
//打印在网页
client.print("temp (C): "); client.println(temp);
client.print("humi (%RH): "); client.println(humi);
}
}
}
// 关闭连接
client.stop();
Serial.println("Client Disconnected.");
}
}
结果
您可以通过手机,电脑等访问网址以获得如下结果(局域网下的温湿度传感器的温湿度)。
成员函数
-
WiFi.softAP(ssid, password)
说明:类似将设置好的WIFI账号密码开热点
参数:- ssid: WIFI名
- password: WIFI密码
-
WiFi.softAPIP()
说明:WIFI的IP地址
8.3 WIFI获取网络时间
本实例演示了从网络时间服务器获取时间,并使用ESP32自带的RTC时钟保持时间更新
本示例来自CSDN博主「Naisu Xu」,原文链接:https://blog.csdn.net/Naisu_kun/article/details/115627629
#include <WiFi.h>
const char *ssid = "********"; //WIFI名称
const char *password = "********"; //WIFI密码
const char *ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 8 * 3600;
const int daylightOffset_sec = 0;
void printLocalTime()
{
struct tm timeinfo;
if (!getLocalTime(&timeinfo))
{
Serial.println("Failed to obtain time");
return;
}
Serial.println(&timeinfo, "%F %T %A"); // 格式化输出
}
void setup()
{
Serial.begin(115200);
Serial.println();
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("WiFi connected!");
// 从网络时间服务器上获取并设置时间
// 获取成功后芯片会使用RTC时钟保持时间的更新
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
printLocalTime();
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
Serial.println("WiFi disconnected!");
}
void loop()
{
delay(1000);
printLocalTime();
}
struct tm结构体
struct tm {
int tm_sec; // 秒,取值0~59;
int tm_min; // 分,取值0~59;
int tm_hour; // 时,取值0~23;
int tm_mday; // 月中的日期,取值1~31;
int tm_mon; // 月,取值0~11;
int tm_year; // 年,其值等于实际年份减去1900;
int tm_wday; // 星期,取值0~6,0为周日,1为周一,依此类推;
int tm_yday; // 年中的日期,取值0~365,0代表1月1日,1代表1月2日,依此类推;
int tm_isdst; // 夏令时标识符,实行夏令时的时候,tm_isdst为正;不实行夏令时的进候,tm_isdst为0;不了解情况时,tm_isdst()为负
};
struct tm结构体格式化输出
格式化字符 | 输出 |
---|---|
%a | 星期几的简写 |
%A | 星期几的全称 |
%b | 月份的简写 |
%B | 月份的全称 |
%c | 标准的日期的时间串 |
%C | 年份的后两位数字 |
%d | 十进制表示的每月的第几天 |
%D | 月/天/年 |
%e | 在两字符域中,十进制表示的每月的第几天 |
%F | 年-月-日 |
%g | 年份的后两位数字,使用基于周的年 |
%G | 年分,使用基于周的年 |
%h | 简写的月份名 |
%H | 24小时制的小时 |
%I | 12小时制的小时 |
%j | 十进制表示的每年的第几天 |
%m | 十进制表示的月份 |
%M | 十时制表示的分钟数 |
%p | 本地的AM或PM的等价显示 |
%r | 12小时的时间 |
%R | 显示小时和分钟:hh:mm |
%S | 十进制的秒数 |
%t | 水平制表符 |
%T | 显示时分秒:hh:mm:ss |
%u | 每周的第几天,星期一为第一天 (值从0到6,星期一为0) |
%U | 第年的第几周,把星期日做为第一天(值从0到53) |
%V | 每年的第几周,使用基于周的年 |
%w | 十进制表示的星期几(值从0到6,星期天为0) |
%W | 每年的第几周,把星期一做为第一天(值从0到53) |
%x | 标准的日期串 |
%X | 标准的时间串 |
%y | 不带世纪的十进制年份(值从0到99) |
%Y | 带世纪部分的十进制年份 |
%z | 时区名称,如果不能得到时区名称则返回空字符 |
8.4 WIFI获取天气
本示例用于让你学会如何获取天气信息并让你体会HTTP中获取的信息通过Json提取数据并打印的
- 烧录代码前我们进行以下几个步骤
1.您需要安装Arduino_JSON库。通过 Arduino IDE Tools -> Manage Libraries 中输入 Arduino_JSON 并安装该库
2.注册OpenWeather的账号以获取我们想要的天气信息 打开浏览器并转到 https://openweathermap.org/appid/ 按注册按钮并创建一个免费帐户。
点击 My API Keys 进入获取API界面
复制这里的Key(这个Key是你从OpenWeather上获取天气信息的唯一钥匙)
你可以将Key填入以下URL并填写城市名以及它的国家以获取城市天气信息
http://api.openweathermap.org/data/2.5/weather?q=yourCityName,yourCountryCode&APPID=yourAPIkey
下面做个示例更换yourCityName你想要的数据的城市(比如成都),yourCountryCode与该城市的国家代码(比如CN),填入yourAPIkey就是前面获得的API密钥,下面为中国成都加上API后的URL:
http://api.openweathermap.org/data/2.5/weather?q=ChengDu,CN&APPID=4de305d0a52ddaceaecba50a757e9968
将你的 URL 复制到您的浏览器中将返回一组与您当地天气相对应的信息。编写本教程的那天,我们获得了以下有关中国成都的天气的信息。
代码
/*
该示例通过学习以了解如何获得天气信息
*/
#include <WiFi.h>
#include <HTTPClient.h>
#include <Arduino_JSON.h>
//修改WIFI名称以及密码
const char* ssid = "******";//WIFI名称
const char* password = "******";//WIFI密码
//填入你获得的API Key
String openWeatherMapApiKey = "4de305d0a52ddaceaecba50a757e9968";
//示例:
//String openWeatherMapApiKey = "4de305d0a52ddaceaecba50a757e9968";
// 填写你的城市名以及国家简写
String city = "ChengDu";
String countryCode = "CN";
//示例:
//String city = "ChengDu";
//String countryCode = "CN";
//设置获取信息的间隔时间,以下用于测试所以设置为10秒
//您应当根据你需要获取数据的网站,规定时间内访问数据的次数上限来限制访问时间的最小间隔
unsigned long lastTime = 0;
//设置每10分钟获得一次天气数据
//unsigned long timerDelay = 600000;
//设置每10秒获得一次天气数据
unsigned long timerDelay = 10000;
String jsonBuffer;
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.println("Connecting");
//判断WIFI是否连接
while(WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to WiFi network with IP Address: ");
Serial.println(WiFi.localIP());
Serial.println("Timer set to 10 seconds (timerDelay variable), it will take 10 seconds before publishing the first reading.");
}
void loop() {
//发送HTTP获取请求
if ((millis() - lastTime) > timerDelay) {
//检测WIFI是否已经连接
if(WiFi.status()== WL_CONNECTED){
String serverPath = "http://api.openweathermap.org/data/2.5/weather?q=" + city + "," + countryCode + "&APPID=" + openWeatherMapApiKey;
//将组合好的URL放入httpGETRequest函数中通过HTTP获取请求以获得文本
jsonBuffer = httpGETRequest(serverPath.c_str());
Serial.println(jsonBuffer);
//将解析的Json对象值储存在Jsonu缓冲区中
JSONVar myObject = JSON.parse(jsonBuffer);
//判断解析是否成功
if (JSON.typeof(myObject) == "undefined") {
Serial.println("Parsing input failed!");
return;
}
Serial.print("JSON object = ");
Serial.println(myObject);
Serial.print("Temperature: ");
//获取到的温度其实是开氏度。
//开氏度 = 摄氏度+273.15
double c = myObject["main"]["temp"];
c = c-273.15;
Serial.println(c);
Serial.print("Pressure: ");
//myObject["main"]["pressure"]前为{}前的引号内容,后为读取哪一个引号后数据
Serial.println(myObject["main"]["pressure"]);
Serial.print("Humidity: ");
Serial.println(myObject["main"]["humidity"]);
Serial.print("Wind Speed: ");
Serial.println(myObject["wind"]["speed"]);
}
else {
Serial.println("WiFi Disconnected");
}
lastTime = millis();
}
}
String httpGETRequest(const char* serverName) {
WiFiClient client;
HTTPClient http;
//连接网址
http.begin(client, serverName);
//发送HTTP站点请求
int httpResponseCode = http.GET();
//该数组用于储存获得的数据
String payload = "{}";
//将获得的数据放入数组
if (httpResponseCode>0) {
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
payload = http.getString();
}
else {
Serial.print("Error code: ");
Serial.println(httpResponseCode);
}
//释放资源
http.end();
//返回获得的数据用于Json处理
return payload;
}
结果
成员函数
-
httpGETRequest(serverPath.c_str());
说明:解析获取的对象 -
JSON.typeof(myObject)
说明:判断对象是否为已被解析的格式
8.5 阿里云IoT
什么是阿里云
阿里云IoT致力于实现万物互联的美好世界,为生态合作伙伴提供基于云端一体化、安全物联网基础平台等,在通过该平台高效连接,管理设备的同时,其开放能力使合作伙伴更高效、低成本地构建各种创新的物联网应用场景。
阿里云物联网平台为设备提供安全可靠的连接通信能力,向下连接海量设备,支撑设备数据采集上云;向上提供云端API,指令数据通过API调用下发至设备端,实现远程控制。
此外阿里云IoT还提供了丰富的开发服务,用户可以直接在该平台上搭建Web可视化、移动应用、服务开发等开发服务,这降低了物联网项目开发的难度,有了它,用户无需任何专业的开发技巧也可开发自己的项目。
- 阿里云IOT请点击这里了解具体详情,使用时需将示例中的#include "DFRobot_Aliyun.h"替换为#include "DFRobot_Iot.h"
8.6 SmartConfig 一键配网+自动重连
通过该代码可使用乐鑫的ESP-TOUCH进行一键配网。
点击下载安卓版
IOS版请在App Store搜索Espressif Esptouch
#include <WiFi.h>
void SmartConfig()
{
WiFi.mode(WIFI_STA);
Serial.println("\r\nWait for Smartconfig...");
WiFi.beginSmartConfig();
while (1)
{
Serial.print(".");
delay(500); // wait for a second
if (WiFi.smartConfigDone())
{
Serial.println("SmartConfig Success");
Serial.printf("SSID:%s\r\n", WiFi.SSID().c_str());
Serial.printf("PSW:%s\r\n", WiFi.psk().c_str());
break;
}
}
}
bool AutoConfig()
{
WiFi.begin();
for (int i = 0; i < 20; i++)
{
int wstatus = WiFi.status();
if (wstatus == WL_CONNECTED)
{
Serial.println("WIFI SmartConfig Success");
Serial.printf("SSID:%s", WiFi.SSID().c_str());
Serial.printf(", PSW:%s\r\n", WiFi.psk().c_str());
Serial.print("LocalIP:");
Serial.print(WiFi.localIP());
Serial.print(" ,GateIP:");
Serial.println(WiFi.gatewayIP());
return true;
}
else
{
Serial.print("WIFI AutoConfig Waiting......");
Serial.println(wstatus);
delay(1000);
}
}
Serial.println("WIFI AutoConfig Faild!" );
return false;
}
void setup() {
Serial.begin(115200);
delay(100);
if (!AutoConfig())
{
SmartConfig();
}
}
void loop() {
}
9.MicroPython教程
9.1 MicroPython环境搭建
想要在Beetle ESP32-C3上运行microPython,首先需要给Beetle ESP32-C3烧录固件
- 点击链接下载microPython固件
- 点击下载esptool Flash烧录工具
- 运行flash_download_tool_3.9.3.exe
- 选择ESP32-C3主控,然后将Beetle ESP32-C3的引脚9连接GND,然后通过USB线连接电脑
- 选择下载的固件,擦除flash后烧录固件
9.2 MicroPython编译器
- 点击下载thonny
- 进入软件,对解释器进行设置(Run->Configure interpreter...)
- 将代码复制到代码框中,保存文件到MicroPython设备中,命名为main.py
import time
from machine import Pin
led=Pin(10,Pin.OUT)
while True:
led.value(1)
time.sleep(1)
led.value(0)
time.sleep(1)
- 重启Beetle ESP32-C3即可看到L灯闪烁
9.3 更多MicroPython教程
常见问题
烧录报错
原因
-
如果Loop中延时过短或者不加延时会导致烧录超时
-
错误的调用一些函数会导致计算机不能识别USB
解决办法
V1.0.0
- 将引脚9连接GND,重新上电,再烧录。
V2.0.0
- 先按住BOOT按键,再点击RST按键,最后松开BOOT按键.
串口无打印
解决办法
- 检查USB CDC是否处于Enable状态
- 使用其他的串口调试助手查看打印信息
更多问题及有趣的应用,可以 访问论坛 进行查阅或发帖。