1. 产品简介
EDGE101是一款基于ESP32内核、具备工业通讯接口与工业级设计的IoT可编程控制器。
ESP32及Arduino是开发者最喜爱的硬件和开发平台之一。它们具有优秀的性能和丰富的开源库。开发者基于ESP32做出了数以万计的创新IoT应用。但是,目前的ESP32大部分开发板都只具备基本的IO口功能,不具备RS485、CAN总线、以太网等实际部署的通讯功能。这导致了不论是学习还是应用,常见的ESP32开发板都有很大的功能缺失。
EDGE101在坚实的金属壳体内设计了RS485、CAN总线、以太网、Wi-Fi、蓝牙、RTC等功能,且支持PCIe插槽扩展4G通讯功能,具备灵活与完善的物联网控制功能。同时,也具备11路ESP32的原生GPIO和3组I2C接口,能让你在一个设备上就完成IoT的学习与应用部署。
市场上的ESP32开发板大都不具备相关的保护措施。这让很多的ESP32的开发板只能停留在桌上的测试层面,很难直接部署在IoT应用项目上。EDGE101按照工业标准设计,采用高质量的金属外壳保护,具备完善的隔离保护、防静电保护、防雷击保护、过压保护、反接保护等电路,9V-26V宽范围供电,且支持导轨和挂耳等多种安装方式。不论是部署在花园、房顶、车库、配电房等场所中均能稳定且灵活的胜任。
EDGE101具备详细的免费在线开发手册,从基本的IO控制、数据采集,到RS485、CAN总线通讯,到Wi-Fi、蓝牙、以太网、4G通讯,以及云服务器实例,均详细讲解。以下是教程部分目录,供参考:
- GPIO控制
- PWM输出
- ADC采集
- 串口通讯
- I2C通讯
- SPI通讯
- TF卡储存及例程
- Timer定时器
- 看门狗定时器
- CAN总线通讯及例程
- RS485通讯及例程
- Wi-Fi通讯及例程
- 蓝牙通讯及例程
- 以太网通讯及例程
- 4G通讯例程
- MQTT例程
更多内容请访问产品的WIKI教程。
2. 产品特性
- ESP32主控,240MHz主频,⽀持WIFI、蓝⽛⽆线通讯。
- ⼯业级设计,具备完善的隔离保护、防静电保护、防雷击保护、过压保护、反接保护等电路。
- ⾦属外壳,有效防⽌⼈员、昆⾍或者动物触碰损坏。
- 带隔离保护的RS485接⼝。
- 带隔离保护的CANBUS接⼝。
- 11路ESP32的原⽣GPIO和4组I2C接⼝。
- ⽀持TF卡插槽,可本地储存数据。
- 内置PCIE扩展插槽以及nano sim卡插槽,⽀持安装4G通讯板卡。
- ⽀持导轨安装、挂⽿螺丝安装、平安装、竖⽴安装等多种安装⽅式。
3. 应用场景
- ⼾外环境数据采集
- 温室数据采集与⾃动化控制
- 仓库、基站、基坑等浸⽔监控
- ⼩型⾃动化设备控制器
- 机器⼈控制器
4. 功能指示图
正面接口说明
No. | 项目 | 描述 |
---|---|---|
1 | 以太网接口(RJ45) | 10/100Mbps |
2 | 复位按钮 | 按下复位 |
3 | 板载按钮 | 自定义按钮 |
4 | LED指示灯 | 自定义LED灯×1、无线通信LED灯×1、电源LED灯×1 |
5 | USB口 | Type-C |
6 | 外部供电输入接口 | DC 9 -26V |
7 | RS485接口 | 推荐波特率9600bps |115200bps |
8 | CAN-Bus接口 | 波特率最大支持1Mbps |
9 | 终端电阻 | 120Ω:RS485×1、CAN-Bus×1 |
10 | Wi-Fi天线 | 2.4G WiFi&蓝牙天线 |
11 | 外接天线 | 产品支持安装其他无线模块(出厂不包括),若是扩展4G模组,则该外接天线为4G 天线。 |
背面接口说明图
No. | 项目 | 描述 |
---|---|---|
1 | 接地端子 | 接地处理 |
2 | SD 卡槽 | 最大支持 32 GB |
3 | SIM卡槽 | 支持Nano SIM |
4 | Gravity GPIO接口 | P5 P12 P14 P15 P18 P23 P33 P34 P37 P38 P39 (共11组) |
5 | Gravity I2C接口 | 3组(提供3V3|5V VCC) |
5. 技术规格
硬件配置
类别 | 参数 |
---|---|
CPU型号 | ESP32双核32位处理器 |
CPU架构 | Xtensa 32-bit LX6 |
CPU主频 | 240MHz,算力高达600MIPS |
片上Flash | 16MB |
内存 | 520KB SRAM,16KB RTC SRAM |
硬件接口
类别 | 数量 | 性能参数 |
---|---|---|
Wi-Fi | 1个 | 2.4G,支持802.11 b/g/n,速度高达150Mbps,支持多种网络模式 |
蓝牙 | 1个 | BT4.2/BLE5.0/BLE MESH组网 |
以太网(RJ45) | 1个 | 10/100Mbps |
按键 | 2个 | 复位按键、板载按键 |
LED指示灯 | 3个 | 1x电源、1x板载自定义、1x无线通信 |
USB口 | 1个 | Type-c |
外部供电输入接口 | 1个 | DC 9-26V |
RS485 | 1路 | 推荐波特率9600bps | 115200bps |
CAN-Bus | 1路 | 波特率最大支持1Mbps |
终端电阻 | 2个 | 120Ω,1xRS485、1xCAN-Bus |
SD 卡槽 | 1个 | 最大支持 32 GB |
SIM卡槽 | 1个 | 支持Nano SIM卡 |
PCIe插槽 | 1个 | 可扩展4G、NB-IOT等通信模块 |
Gravity I2C接口 | 3个 | 支持标准模式100Kbps|快速模式400Kbps |
Gravity GPIO接口 | 11个 | 可扩展继电器、数字和模拟传感器等模块,可配置为输入、输出或ADC |
电气参数
类别 | 参数 |
---|---|
工作电压 | DC 9V~26V 或USB Type-c 5V 2A供电 |
电源保护 | 具备雷击浪涌保护、反接保护 |
防护 | 雷击浪涌 2KV,静电接触 6KV |
工作温度 | -20~+75℃ |
环境湿度 | 5~90%RH(无凝结) |
运行能力 | 支持 7*24h 不间断工作 |
静态功耗 | 50mA@电源端子 12.0V|100mA@USB 5.0V |
尺寸 | 136.7mm×76mm×31mm |
重量(裸板) | 63g |
重量(整机) | 335g |
6. 产品安装图
产品支持增加挂耳和导轨两种安装方式,如下图所示。
安装挂耳:
安装DIN轨道:
导轨安装效果
7. 基础使用教程
7.1 软件准备
-
下载Arduino IDE: 点击下载Arduino IDE
-
配置Arduino IDE编译环境
第一步:打开Arduino IDE->File->Preferences,在弹出的窗口里,点击这个像文档的按钮。
第二步:复制这个网址链接,https://downloadcd.dfrobot.com.cn/DFRobot_Edge101/package_Edge101_index.json
,并粘贴到方框中。 最后点击OK即可。
第三步:打开Tools->Board->Boards Manager...,如下图所示:
第四步:在方框内输入“Edge101”,找到下图中的资源包,点击“Install”,等待资源包下载完毕。最后点击“close”。
第五步:最后设置板卡为“Edge101 IOT Controller”,点击Tool->DFRobot Edge101->Edge101 IOT Controller。
至此,您已完成Edge101 SDK的安装,可以开始给Edge101编写和烧录代码了!
7.2 GPIO
Edge101主板上的40PIN扩展接口提供11个GPIO,并配有Gravity 3PIN接口/4PIN I2C接口,可直接连接DFRobot的Gravity设备。GPIO支持内部上拉/下拉或高阻配置,作为输入时可通过读取寄存器获取值,并支持边缘或电平触发生成CPU中断。所有IO管脚均为双向、非反相、三态设计,具备输入、输出及三态控制功能,且可复用为SDIO、UART、SPI等其他功能。
40PIN扩展接口图
引脚名称 | GPIO功能 | ADC功能 | 通信功能 | 复用功能 |
---|---|---|---|---|
P5 | GPIO5 可作为输 入和输出 | SPICS0 | Gravity SPICS0 | |
P12 | GPIO12 可作为输 入和输出 | ADC2_CH5 | SPI-SDO | Gravity SPI-SDO |
P14 | GPIO14 可作为输 入和输出 | ADC2_CH6 | SPI-CLK | Gravity SPI-CLK |
P15 | GPIO15 可作为输 入和输出 | ADC2_CH3 | 板载LED | |
P18 | GPIO18 可作为输 入和输出 | I2C-SDA | Gravity I2C-SDA | |
P23 | GPIO23 可作为输 入和输出 | I2C-SCL | Gravity I2C-SCL | |
P33 | GPIO33 可作为输 入和输出 | U1TXD | PCIe插 槽U1TXD | |
P34 | GP IO34只能 作为输入 | U1RXD | PCIe插 槽U1RXD | |
P37 | GP IO37只能 作为输入 | ADC1_CH1 | ||
P38 | GP IO38只能 作为输入 | ADC1_CH2 | 板载按钮 | |
P39 | GP IO39只能 作为输入 | ADC1_CH3 | SPI-SDI | Gravity SPI-SDI |
注意:
- GPIO34 ~ GPIO38只能用于输入,而且PULLUP 和 PULLDOWN模式都不具备。 在模拟输入时,你必须使用连接到 ADC 的 GPIO。但是,如果你使用无线网络,比如 WiFi,你将无法使用 ADC 2。
- 以下GPIO已经在内部具有上下拉功能: GPIO0:内部上拉 ;GPIO5:内部上拉 ;GPIO12:内部下拉。其中,GPIO12内部已下拉,默认为FLASH工作于3.3V,若外部电路强制上拉,可能导致主板异常工作。
例程:控制LED灯
将连接板载按钮的P38设置为输入,连接板载LED的P15设置为输出,当按钮按下时LED灯亮。
硬件准备: ESP32 IoT 可编程控制器(SKU:DFR0886)×1
示例代码:
const int buttonPin = 38; // 板载按钮的GPIO38
const int ledPin = 15; // 板载LED的GPIO15
// 变量将更改
int buttonState = 0; // 使用一个变量来保存按钮状态
void setup() {
//如果外部电路没有设计上拉或下拉电阻,当GPIO作为输入时需要将内部上拉或下拉电阻使能,以便准确识别电平信号。
//初始化button pin为输入,不上拉也不下拉 pinMode(buttonPin, INPUT);
// 初始化button pin为输入,并且上拉使能 pinMode(buttonPin, INPUT_PULLUP)
// 初始化button pin为输入,并且下拉使能 pinMode(buttonPin, INPUT_PULLDOWN);
pinMode(ledPin, OUTPUT); // 初始化LED pin为输出:
pinMode(buttonPin, INPUT); // 初始化button pin为输入
}
void loop() {
// 读取按钮状态
buttonState = digitalRead(buttonPin);
// 如果按钮输入为低电平代表按钮按下,点亮主板上绿色用户LED
if (buttonState == LOW) {
// 点亮LED
digitalWrite(ledPin, LOW);
} else {
// 关闭LED
digitalWrite(ledPin, HIGH);
}
}
运行结果:按下按键KEY(P38)时,LED(P15)点亮,松开按键则熄灭。
7.3 PWM
例程:PWM调节LED亮度
让Edge101主板上的用户LED产生渐变的亮灭效果。通过 PWM 调节 LED 亮度,频率 5000Hz。
硬件准备: ESP32 IoT 可编程控制器(SKU:DFR0886)×1
示例代码:
// PWM 通道配置
#define PWM_CH 0 // 使用 PWM 通道 0
#define PWM_FREQ 5000 // 设置 PWM 频率为 5kHz
#define PWM_RES 13 // 13位分辨率(范围 0-8191)
#define LED_PIN 15 // LED 连接引脚
// 呼吸灯控制参数
int brightness = 0; // 当前亮度值(0-255)
int fadeAmount = 5; // 亮度变化步长
/**
* 自定义 PWM 输出函数
* @param ch PWM 通道
* @param val 亮度值(0-255)
* @param maxVal 输入值最大值(默认8位)
*/
void ledcAnalogWrite(uint8_t ch, uint32_t val, uint32_t maxVal = 255) {
// 将 8位亮度值转换为 13位占空比
ledcWrite(ch, (8191 / maxVal) * min(val, maxVal));
}
void setup() {
// 初始化 PWM 配置
ledcSetup(PWM_CH, PWM_FREQ, PWM_RES); // 设置 PWM 参数
ledcAttachPin(LED_PIN, PWM_CH); // 绑定引脚到 PWM 通道
}
void loop() {
// 输出当前亮度值
ledcAnalogWrite(PWM_CH, brightness);
// 更新亮度值(线性渐变)
brightness += fadeAmount;
// 到达亮度极值时反转渐变方向
if (brightness <= 0 || brightness >= 255) {
fadeAmount = -fadeAmount;
}
delay(30); // 控制呼吸灯变化速度
}
7.4 ADC
Edge101主板具备 12-bit SAR ADC,共支持6 个模拟通道输入,采样速度2 Msps。同一时间每个ADC只能采集一个通道。注意:在WiFi开启时ADC2不能使用。
例程:ADC单通道采集
硬件准备:
- ESP32 IoT 可编程控制器(SKU:DFR0886)×1
- Gravity:模拟角度传感器 (SKU:DFR0054)×1
示例代码:
void setup()
{
Serial.begin(115200);
Serial.println();
}
void loop()
{
int vtmp = analogRead(37); //GPIO37 ADC1_CH1获取电压
Serial.printf("sample value: %d\n", vtmp);
Serial.printf("voltage: %.3fV\n", vtmp * 3.26 / 4095);
delay(500);
}
运行结果:将程序烧写到主板后,在P37脚连接一个电位器,调节输入的电压。串口将打印出测量到的原始电压数字量和电压值。
7.5 串口
Edge101主板有三个串口,分别是Serial、Serial1、Serial2。其中Serial连接到USB接口用于程序下载和代码调试,在上电时会输出主板基本信息。Serial1用于扩展板载无线模块或外置串口设备。Serial2用于RS485接口连接工业传感器和执行器等设备。
例程:串口读取用户按钮状态
以下代码演示功能:读取引脚38的数字输入信号,并将结果输出到串口监视器。
硬件准备:ESP32 IoT 可编程控制器(SKU:DFR0886)×1
示例代码:
/*数字输入读取示例
*/
int pushButton = 38;
// 初始化函数(复位时运行一次):
void setup() {
// 以115200波特率初始化串口通信:
Serial.begin(115200);
// 将按钮引脚设置为输入模式:
pinMode(pushButton, INPUT);
}
// 主循环函数(重复执行):
void loop() {
// 读取输入引脚的状态:
int buttonState = digitalRead(pushButton);
// 将按钮状态输出到串口:
Serial.println(buttonState);
delay(1); // 读取间隔延时(确保稳定性)
}
运行结果:当主板上的用户按钮没有按下时串口打印出1,当按钮按下串口打印出0。
7.6 I2C
Edge101主板有两个 I2C 控制端口,负责处理在 I2C 两根总线上的通信,每个控制器都可以设置为主机或从机。主板扩展接口使用GPIO18 作为I2C SDA,GPIO 23 作为 I2C SCL,并且使用和Arduino Wire兼容的库。
例程:扫描I2C总线上连接的设备
每隔5秒扫描I2C总线上的设备,并将扫描到的从机设备地址从串口打印出来。
硬件准备:ESP32 IoT 可编程控制器(SKU:DFR0886)×1
示例代码:
#include "Wire.h" // 包含I2C通信库
void setup() {
Serial.begin(115200); // 初始化串口通信(波特率115200)
Wire.begin(); // 初始化I2C总线(默认使用GPIO18-SDA/GPIO23-SCL)
}
void loop() {
byte error, address; // error: 通信状态码,address: 扫描地址
int nDevices = 0; // 发现设备计数器
delay(5000); // 每5秒扫描一次总线
Serial.println("Scanning for I2C devices ...");
// 扫描标准I2C地址范围(0x01~0x7F)
for(address = 0x01; address < 0x7f; address++) {
Wire.beginTransmission(address); // 尝试与目标地址通信
error = Wire.endTransmission(); // 获取通信状态
if (error == 0) { // 状态码0表示设备响应
Serial.printf("I2C device found at address 0x%02X\n", address);
nDevices++;
}
else if(error != 2) { // 状态码2是正常未连接状态,其他错误需提示
Serial.printf("Error %d at address 0x%02X\n", address, error);
}
}
if (nDevices == 0) { // 未发现任何设备
Serial.println("未检测到I2C设备");
}
}
运行结果:串口打印出扫描到的设备,0x51地址是主板上RTC芯片的I2C地址。
7.7 SPI
例程:驱动SPI显示屏
在Edge101主板的SPI接口上连接一个DFR0664的2.0寸TFT液晶屏。
首先我们需要安装 DFRobot_GDL 库(详见TFT液晶 wiki),然后打开 DFRobot_GDL->example->Basic->UI_bar例程例程:驱动SPI显示屏
硬件准备:
- ESP32 IoT 可编程控制器(SKU:DFR0886)×1
- 2.0寸TFT液晶屏(SKU:DFR0664)×1
硬件连接:
液晶VCC连接到 Edge101 主板的 +5V //注意:液晶屏需要5V供电
液晶GND连接到 Edge101 主板的 GND
液晶CK 连接到 Edge101 主板SPI接口的 SCK(P14)
液晶SI 连接到 Edge101 主板SPI接口的 SDO(P12)
液晶SO 连接到 Edge101 主板SPI接口的 SDI(P39)
液晶BL 连接到 Edge101 主板的 3V3
液晶DC 连接到 Edge101 主板的 P15
液晶CS 连接到 Edge101 主板的 P5
液晶RT 连接到 Edge101 主板的 P33
示例代码:这是一个加载控件的显示示例,显示了三种不同的加载控件。
/*!
* @file UI_bar.ino
* @brief Create a progress bar control on the screen.
* @n Users can customize the parameters of the progress bar or use the default parameters.
* @n Users can control the value of the progress bar through the callback function of the progress bar.
* @n The example supports Arduino Uno, Leonardo, Mega2560, FireBeetle-ESP32, FireBeetle-ESP8266, FireBeetle-M0.
* @copyright Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com)
* @licence The MIT License (MIT)
* @author [fengli](li.feng@dfrobot.com)
* @version V1.0
* @date 2019-12-6
* @get from https://www.dfrobot.com
* @url https://github.com/DFRobot/DFRobot_GDL/src/DFRpbot_UI
*/
#include "DFRobot_UI.h"
#include "DFRobot_GDL.h"
/*M0*/
#if defined ARDUINO_SAM_ZERO
#define TFT_DC 7
#define TFT_CS 5
#define TFT_RST 6
/*ESP32 and ESP8266*/
#elif defined(ESP32) || defined(ESP8266)
// 液晶VCC连接到 Edge101WE 主板的 +5V(40PIN扩展接口的PIN2)
// 液晶GND连接到 Edge101WE 主板的 GND
// 液晶CK 连接到 Edge101WE 主板SPI接口的 SCK
// 液晶SI 连接到 Edge101WE 主板SPI接口的 SDO
// 液晶SO 连接到 Edge101WE 主板SPI接口的 SDI
// 液晶BL 连接到 Edge101WE 主板的 3V3
// 液晶DC 连接到 Edge101WE 主板的GPIO 15
// 液晶CS 连接到 Edge101WE 主板的GPIO 5
// 液晶RT 连接到 Edge101WE 主板的GPIO 33
#define TFT_DC 15
#define TFT_CS 5
#define TFT_RST 33
/*AVR series mainboard*/
#else
#define TFT_DC 2
#define TFT_CS 3
#define TFT_RST 4
#endif
/**
* @brief Constructor Constructors for hardware SPI communication
* @param dc Command pin or data line pin of SPI communication
* @param cs Chip select pin for SPI communication
* @param rst Reset pin of the screen
* @param bl Screen backlight pin
*/
//DFRobot_ST7789_240x240_HW_SPI screen(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST);
DFRobot_ST7789_240x320_HW_SPI screen(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST);
//DFRobot_ILI9341_240x320_HW_SPI screen(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST);
//DFRobot_ILI9488_320x480_HW_SPI screen(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST);
/* M0 mainboard DMA transfer */
//DFRobot_ST7789_240x240_DMA_SPI screen(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST);
//DFRobot_ST7789_240x320_DMA_SPI screen(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST);
//DFRobot_ILI9341_240x320_DMA_SPI screen(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST);
//DFRobot_ILI9488_320x480_DMA_SPI screen(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST);
/**
* @brief Construct a function
* @param gdl Screen object
* @param touch Touch object
*/
DFRobot_UI ui(&screen, NULL);
uint8_t value1 = 0;
uint8_t value2 = 0;
uint8_t value3 = 0;
//Callback function of progress bar1
void barCallback1(DFRobot_UI:: sBar_t &obj){
//Enable the progress bar plus 1 in each time, it enters the callback function.
delay(50);
obj.setValue(value1);
if(value1 < 100) value1++;
}
//Callback function of progress bar2
void barCallback2(DFRobot_UI:: sBar_t &obj){
//Enable the progress bar plus 1 in each time, it enters the callback function.
delay(50);
delay(50);
obj.setValue(value2);
if(value2 < 100) value2++;
}
//Callback function of progress bar3
void barCallback3(DFRobot_UI:: sBar_t &obj){
//Enable the progress bar plus 1 in each time, it enters the callback function.
delay(50);
delay(50);
obj.setValue(value3);
if(value3 < 100) value3++;
}
void setup()
{
Serial.begin(9600);
//Initialize UI
ui.begin();
ui.setTheme(DFRobot_UI::MODERN);
//Display a string on the screen
ui.drawString(/*x=*/33,/*y=*/screen.height()/5*4,"Page of loading",COLOR_RGB565_WHITE,ui.bgColor,/*fontsize =*/2,/*Invert=*/0);
//Create a progress bar control
DFRobot_UI::sBar_t &bar1 = ui.creatBar();
/** User-defined progress bar parameter **/
bar1.setStyle(DFRobot_UI::COLUMN);
bar1.fgColor = COLOR_RGB565_GREEN;
bar1.setCallback(barCallback1);
ui.draw(&bar1,/*x=*/33,/*y=*/screen.height()/5*3);
DFRobot_UI::sBar_t &bar2 = ui.creatBar();
/**User-defined progress bar parameter*/
bar2.setStyle(DFRobot_UI::CIRCULAR);
bar2.setCallback(barCallback2);
ui.draw(&bar2,/*x=*/120,/*y=*/screen.height()/5*2);
DFRobot_UI::sBar_t &bar3 = ui.creatBar();
/**User-defined progress bar parameter*/
bar3.fgColor = COLOR_RGB565_BLUE;
bar3.setStyle(DFRobot_UI::BAR);
bar3.setCallback(barCallback3);
ui.draw(&bar3,/*x=*/(screen.width()-bar3.width)/2,/*y=*/screen.height()/10);
}
void loop()
{
//Refresh
ui.refresh();
}
运行结果:显示结果如下。
7.8 SD存储
注意:下载例程时 Partition Scheme 分区方案 选择带FAT系统的分区。
例程:读取SD卡
首先挂载SD卡,如果挂载失败会打印错误,然后检测SD卡类型。接下来对SD卡进行一系列的操作。
硬件准备:ESP32 IoT 可编程控制器(SKU:DFR0886)×1
示例代码:
/*
* Connect the SD card to the following pins:
*
* SD Card | FireBeetle MESH
* D2 -
* D3 GPIO5
* CMD MOSI
* VSS GND
* VDD 3.3V
* CLK SCK
* VSS GND
* D0 MISO
* D1 -
*/
#include "FS.h"
#include "SD.h"
#include "SPI.h"
void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
Serial.printf("Listing directory: %s\n", dirname);
File root = fs.open(dirname);
if(!root){
Serial.println("Failed to open directory");
return;
}
if(!root.isDirectory()){
Serial.println("Not a directory");
return;
}
File file = root.openNextFile();
while(file){
if(file.isDirectory()){
Serial.print(" DIR : ");
Serial.println(file.name());
if(levels){
listDir(fs, file.name(), levels -1);
}
} else {
Serial.print(" FILE: ");
Serial.print(file.name());
Serial.print(" SIZE: ");
Serial.println(file.size());
}
file = root.openNextFile();
}
}
void createDir(fs::FS &fs, const char * path){
Serial.printf("Creating Dir: %s\n", path);
if(fs.mkdir(path)){
Serial.println("Dir created");
} else {
Serial.println("mkdir failed");
}
}
void removeDir(fs::FS &fs, const char * path){
Serial.printf("Removing Dir: %s\n", path);
if(fs.rmdir(path)){
Serial.println("Dir removed");
} else {
Serial.println("rmdir failed");
}
}
void readFile(fs::FS &fs, const char * path){
Serial.printf("Reading file: %s\n", path);
File file = fs.open(path);
if(!file){
Serial.println("Failed to open file for reading");
return;
}
Serial.print("Read from file: ");
while(file.available()){
Serial.write(file.read());
}
file.close();
}
void writeFile(fs::FS &fs, const char * path, const char * message){
Serial.printf("Writing file: %s\n", path);
File file = fs.open(path, FILE_WRITE);
if(!file){
Serial.println("Failed to open file for writing");
return;
}
if(file.print(message)){
Serial.println("File written");
} else {
Serial.println("Write failed");
}
file.close();
}
void appendFile(fs::FS &fs, const char * path, const char * message){
Serial.printf("Appending to file: %s\n", path);
File file = fs.open(path, FILE_APPEND);
if(!file){
Serial.println("Failed to open file for appending");
return;
}
if(file.print(message)){
Serial.println("Message appended");
} else {
Serial.println("Append failed");
}
file.close();
}
void renameFile(fs::FS &fs, const char * path1, const char * path2){
Serial.printf("Renaming file %s to %s\n", path1, path2);
if (fs.rename(path1, path2)) {
Serial.println("File renamed");
} else {
Serial.println("Rename failed");
}
}
void deleteFile(fs::FS &fs, const char * path){
Serial.printf("Deleting file: %s\n", path);
if(fs.remove(path)){
Serial.println("File deleted");
} else {
Serial.println("Delete failed");
}
}
void testFileIO(fs::FS &fs, const char * path){
File file = fs.open(path);
static uint8_t buf[512];
size_t len = 0;
uint32_t start = millis();
uint32_t end = start;
if(file){
len = file.size();
size_t flen = len;
start = millis();
while(len){
size_t toRead = len;
if(toRead > 512){
toRead = 512;
}
file.read(buf, toRead);
len -= toRead;
}
end = millis() - start;
Serial.printf("%u bytes read for %u ms\n", flen, end);
file.close();
} else {
Serial.println("Failed to open file for reading");
}
file = fs.open(path, FILE_WRITE);
if(!file){
Serial.println("Failed to open file for writing");
return;
}
size_t i;
start = millis();
for(i=0; i<2048; i++){
file.write(buf, 512);
}
end = millis() - start;
Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end);
file.close();
}
void setup(){
Serial.begin(115200);
// GPIO5连接SD卡CS
if(!SD.begin(5)){
Serial.println("Card Mount Failed");
return;
}
uint8_t cardType = SD.cardType();
if(cardType == CARD_NONE){
Serial.println("No SD card attached");
return;
}
Serial.print("SD Card Type: ");
if(cardType == CARD_MMC){
Serial.println("MMC");
} else if(cardType == CARD_SD){
Serial.println("SDSC");
} else if(cardType == CARD_SDHC){
Serial.println("SDHC");
} else {
Serial.println("UNKNOWN");
}
uint64_t cardSize = SD.cardSize() / (1024 * 1024);
Serial.printf("SD Card Size: %lluMB\n", cardSize);
listDir(SD, "/", 0);
createDir(SD, "/mydir");
listDir(SD, "/", 0);
removeDir(SD, "/mydir");
listDir(SD, "/", 2);
writeFile(SD, "/hello.txt", "Hello ");
appendFile(SD, "/hello.txt", "World!\n");
readFile(SD, "/hello.txt");
deleteFile(SD, "/foo.txt");
renameFile(SD, "/hello.txt", "/foo.txt");
readFile(SD, "/foo.txt");
testFileIO(SD, "/test.txt");
Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));
}
void loop(){
}
运行结果:串口打印信息如下。
8. 进阶使用教程
8.1 Timer定时器
例程:RepeatTimer
程序使用定时器0进行定时,并在串口打印出计时结果。当按下 P38 上的用户按钮后,定时结束。
硬件准备:ESP32 IoT 可编程控制器(SKU:DFR0886)×1
示例代码:
/*
重复定时器示例
本示例展示了如何在ESP32中使用硬件定时器。定时器每秒调用一次
onTimer 函数。定时器可以通过连接到 PIN 0 (IO0) 的按钮停止。
此示例代码属于公共领域(public domain)。
*/
// 停止按钮连接到 PIN 38 (IO38)
#define BTN_STOP_ALARM 38
hw_timer_t * timer = NULL;
volatile SemaphoreHandle_t timerSemaphore;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
volatile uint32_t isrCounter = 0;
volatile uint32_t lastIsrAt = 0;
void ARDUINO_ISR_ATTR onTimer(){
// 递增计数器并记录中断发生时间
portENTER_CRITICAL_ISR(&timerMux);
isrCounter++;
lastIsrAt = millis();
portEXIT_CRITICAL_ISR(&timerMux);
// 释放信号量,以便在 loop 中进行检查
xSemaphoreGiveFromISR(timerSemaphore, NULL);
// 如果需要切换输出状态,在此使用 digitalRead/Write 是安全的
}
void setup() {
Serial.begin(115200);
// 设置 BTN_STOP_ALARM 为输入模式
pinMode(BTN_STOP_ALARM, INPUT);
// 创建信号量,以通知定时器已触发
timerSemaphore = xSemaphoreCreateBinary();
// 使用 4 个定时器中的第 1 个(从 0 开始计数)
// 设置预分频值为 80
// 主板当前时钟频率为 80MHz,80 分频后计数单位是微秒
timer = timerBegin(0, 80, true);
// 绑定 onTimer 函数到定时器
timerAttachInterrupt(timer, &onTimer, true);
// 设置定时器报警,每秒调用一次 onTimer 函数(值以微秒为单位)
// 使报警重复触发(第三个参数)
timerAlarmWrite(timer, 1000000, true);
// 启动定时器报警
timerAlarmEnable(timer);
}
void loop() {
// 如果定时器已触发
if (xSemaphoreTake(timerSemaphore, 0) == pdTRUE){
uint32_t isrCount = 0, isrTime = 0;
// 读取中断计数和时间
portENTER_CRITICAL(&timerMux);
isrCount = isrCounter;
isrTime = lastIsrAt;
portEXIT_CRITICAL(&timerMux);
// 打印输出
Serial.print("onTimer 触发次数: ");
Serial.print(isrCount);
Serial.print(" 时间: ");
Serial.print(isrTime);
Serial.println(" ms");
}
// 如果按钮被按下
if (digitalRead(BTN_STOP_ALARM) == LOW) {
// 如果定时器仍在运行
if (timer) {
// 停止并释放定时器
timerEnd(timer);
timer = NULL;
}
}
}
运行结果:串口打印数据,当按下主板上的用户按钮计时器停止计时。
8.2 看门狗定时器
例程:任务监视器计时器(TWDT)
例程中设置看门狗溢出时间3秒,在前10秒内每2秒复位一次看门狗,10秒后停止复位看门狗,此时看门狗定时器将溢出,主板复位。
硬件准备:ESP32 IoT 可编程控制器(SKU:DFR0886)×1
示例代码:
#include <esp_task_wdt.h>
// 3 秒看门狗定时器(WDT)
#define WDT_TIMEOUT 3
void setup() {
Serial.begin(115200);
Serial.println("正在配置 WDT...");
esp_task_wdt_init(WDT_TIMEOUT, true); // 启用 panic,触发 WDT 超时时 ESP32 自动重启
esp_task_wdt_add(NULL); // 将当前线程添加到 WDT 监控
}
int i = 0; // WDT 重置计数器
int last = millis(); // 记录上次重置时间
void loop() {
// 每 2 秒重置 WDT,执行 5 次后停止
if (millis() - last >= 2000 && i < 5) {
Serial.println("正在重置 WDT...");
esp_task_wdt_reset(); // 重置看门狗定时器
last = millis(); // 更新重置时间
i++; // 计数器加一
// 第 5 次重置后停止重置,等待 WDT 超时重启
if (i == 5) {
Serial.println("停止 WDT 重置,CPU 将在 3 秒内重启");
}
}
}
8.3 RS485通信
例程:RS485串口数据透传
这里使用 AccessPort 软件作为发送和接收串口信息的调试软件。
将代码烧录到一个主板后,通过任意USB/RS485/TTL 协议转换器发送数据给主板。
硬件准备:
- ESP32 IoT 可编程控制器(SKU:DFR0886)×1
- USB/RS485/TTL 协议转换器(SKU:TEL0070) ×1
示例代码:程序将主板USB串口收到的数据通过RS485接口发送出去,同时接收RS485的数据后通过USB串口发送出去。
#include <ArduinoRS485.h>
RS485Class RS485;
void setup() {
Serial.begin(9600);
RS485.begin(9600); // 初始化 RS485,波特率为 9600
}
bool flag = false;
void loop() {
// 如果串口有数据可读
while (Serial.available()) {
if (!flag) flag = true; // 标志置为 true,表示开始传输
if (flag) RS485.beginTransmission(); // RS485 开始传输
RS485.write((char)Serial.read()); // 读取串口数据并通过 RS485 发送
}
// 如果已开始传输,结束 RS485 传输
if (flag) RS485.endTransmission();
flag = false; // 重置标志位
// 如果 RS485 有数据可读
while (RS485.available()) {
Serial.write(RS485.read()); // 读取 RS485 数据并发送到串口
}
}
烧录完成之后,并且示例中主板的USB串口号是COM7。
在主板的RS485接口上连接一个USB/RS485/TTL 协议转换器,转换器的USB连接到电脑,此时弹出一个USB串口COM8。
从主板的USB串口COM7输入字符串 “Data sent by motherboard”,RS485转换器COM8收到发送的字符串。
RS485转换器COM8输入字符串 “Data sent by RS485 device”,主板的USB串口COM7收到发送的字符串。
8.4 CAN通信
8.4.1 例程:CAN Bus 接收
本例程展示了 Edge101主板使用 CAN Bus 来接收帧数据,并且判断帧数据是什么类型。用户还可以通过用户按键来关闭 CAN Bus 功能。
硬件准备:
- ESP32 IoT 可编程控制器(SKU:DFR0886)×1
- USB-CAN转换器 ×1
硬件连接:
示例代码:
#include "DFRobot_ESP32CAN.h"
DFRobot_ESP32CAN ESP32Can;
uint8_t userKey = 38;
can_message_t message;
//外部中断函数,关闭CAN
void interEvent(void){
ESP32Can.clearReceiveQueue(); //清空RX队列
ESP32Can.stop(); //停止CAN驱动阻止进一步收发
ESP32Can.release(); //卸载CAN驱动
detachInterrupt(userKey); //卸载设置的中断引脚
}
void setup() {
pinMode(userKey, INPUT_PULLUP);
attachInterrupt(userKey,interEvent,CHANGE);
Serial.begin(115200);
/*请到 DFRobot_ESP32CAN.h 中找到 can_general_config_t 这个结构体内容和 CAN_GENERAL_CONFIG_DEFAULT(op_mode) 进一步了解和修改部分内容。
函数功能:更改CAN的模式
模式
CAN_MODE_NORMAL 正常 发送/接收/确认 模式
CAN_MODE_NO_ACK 发送无确认传输模式,用于自检
CAN_MODE_LISTEN_ONLY 不影响总线模式(不会发送和确认但可以接收消息)
*/
can_general_config_t g_config = CAN_GENERAL_CONFIG(CAN_MODE_NORMAL); //将更改好的内容给 g_config 一般配置,用于初始化
/*请到 DFRobot_ESP32CAN.h 中找到 can_timing_config_t 这个结构体内容和 CAN_TIMING_CONFIG_500KBITS() 进一步了解和修改部分内容。
函数功能:更改波特率
可选波特率:
CAN_TIMING_CONFIG_25KBITS()
CAN_TIMING_CONFIG_50KBITS()
CAN_TIMING_CONFIG_100KBITS()
CAN_TIMING_CONFIG_125KBITS()
CAN_TIMING_CONFIG_250KBITS()
CAN_TIMING_CONFIG_500KBITS()
CAN_TIMING_CONFIG_800KBITS()
CAN_TIMING_CONFIG_1MBITS()
*/
can_timing_config_t t_config = CAN_TIMING_CONFIG_500KBITS(); //将设置好内容给 t_config 定时配置,用于初始化
/* 请到 DFRobot_ESP32CAN.h 中找到 can_filter_config_t 这个结构体内容和 CAN_FILTER_CONFIG_ACCEPT_ALL() 进一步了解和修改部分内容。
CAN_FILTER_CONFIG_ACCEPT_ALL() {.acceptance_code = 0, .acceptance_mask = 0xFFFFFFFF, .single_filter = true}
函数功能:配置过滤器,默认屏蔽位全部为1,这表示标识符必须完全一样才会接收。
*/
can_filter_config_t f_config = CAN_FILTER_CONFIG_ACCEPT_ALL();
//将配置好的参数放入初始化函数
while(!ESP32Can.init(&g_config,&t_config,&f_config)){
Serial.println("CAN init err!!!");
delay(1000);
}
//安装CAN驱动,重置RX、TX队列以及RX接收消息的计数
while(!ESP32Can.start()){
Serial.println("CAN start err!!!");
delay(1000);
}
}
void loop() {
// 接收信息函数,message 为总线上的报文,pdMS_TO_TICKS() 设置阻塞,就是等待自己想要的数据的规定时间,单位1ms
if(ESP32Can.receive(&message,pdMS_TO_TICKS(1000))){
// 所要接收id,可通过添加||来允许接收多个ID
if(message.identifier == 0x0006){
/*可到 DFRobot_ESP32CAN.h 中找到 Message flags 查看
flags:
CAN_MSG_FLAG_NONE 标准格式
CAN_MSG_FLAG_EXTD 扩展格式
CAN_MSG_FLAG_RTR 遥控帧(远程帧)
CAN_MSG_FLAG_SS 报错不重发
CAN_MSG_FLAG_SELF 自接收请求
*/
if (message.flags == CAN_MSG_FLAG_NONE) {
Serial.println("Message is in Standard Format");
} else if(message.flags == CAN_MSG_FLAG_EXTD){
Serial.println("Message is in Extended Format");
} else if(message.flags == CAN_MSG_FLAG_RTR){
Serial.println("Message is a Remote Transmit Request");
} else if(message.flags == CAN_MSG_FLAG_SS){
Serial.println("Transmit as a Single Shot Transmission");
} else if(message.flags == CAN_MSG_FLAG_SELF){
Serial.println("Transmit as a Self Reception Request");
} else if(message.flags == CAN_MSG_FLAG_DLC_NON_COMP){
Serial.println("Message's Data length code is larger than 8. This will break compliance with CAN2.0B");
}
//message.data_length_code 在 g_config 中已默认为 5字符作为传输字符数
for (int i = 0; i < message.data_length_code; i++) {
Serial.printf("Data byte %d = %d\n", i, message.data[i]);
}
}else{
printf("err id:%d\n",message.identifier);
}
} else{
Serial.println("Failed to queue message for receive");
}
delay(10);
}
烧录完成后,通过USB CAN分析器发送数据给主板。
运行结果:主板A的CAN总线接收到数据并通过USB串口输出。
8.4.2 例程:CAN Bus发送
本例程展示了 Edge101主板使用 CAN Bus 来发送标准帧数据,并且可通过用户按键关闭 CAN Bus 功能。
硬件准备:
- ESP32 IoT 可编程控制器(SKU:DFR0886)×1
- USB-CAN转换器 ×1
硬件连接:
示例代码:
/*!
* @file can_send.ino
* @brief 本demo展示了FireBeetle MESH - Industrial IoT Mainboard 使用CAN来发送标准帧数据,并且可通过用户按键关闭CAN功能
* @copyright Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com)
* @licence The MIT License (MIT)
* @author [yangfeng]<feng.yang@dfrobot.com>
* @version V1.0
* @date 2021-04-08
* @get from https://www.dfrobot.com
*/
#include "DFRobot_ESP32CAN.h"
DFRobot_ESP32CAN ESP32Can;
uint8_t userKey = 38;
can_message_t tx_message;
void interEvent(void){
ESP32Can.stop();
ESP32Can.release();
detachInterrupt(userKey);
}
void setup() {
pinMode(userKey, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(userKey),interEvent,CHANGE); //Enable external interrupts
Serial.begin(9600);
/** 这里对can_general_config_t这个结构体内容进行注释说明,对于 MESH 开发板我们默认CAN的收发IO口为GPIO_35、GPIO_32
*
* typedef struct {
* can_mode_t mode; < Mode of CAN controller
* gpio_num_t tx_io; < Transmit GPIO number
* gpio_num_t rx_io; < Receive GPIO number
* gpio_num_t clkout_io; < CLKOUT GPIO number (optional, set to -1 if unused)
* gpio_num_t bus_off_io; < Bus off indicator GPIO number (optional, set to -1 if unused)
* uint32_t tx_queue_len; < Number of messages TX queue can hold (set to 0 to disable TX Queue)
* uint32_t rx_queue_len; < Number of messages RX queue can hold
* uint32_t alerts_enabled; < Bit field of alerts to enable
* uint32_t clkout_divider; < CLKOUT divider. Can be 1 or any even number from 2 to 14 (optional, set to 0 if unused)
* int intr_flags; < Interrupt flags to set the priority of the driver's ISR. Note that to use the ESP_INTR_FLAG_IRAM, the CONFIG_CAN_ISR_IN_IRAM option should be enabled first.
* } can_general_config_t;
*
* mode:
* CAN_MODE_NORMAL, < Normal operating mode where CAN controller can send/receive/acknowledge messages
* CAN_MODE_NO_ACK, < Transmission does not require acknowledgment. Use this mode for self testing
* CAN_MODE_LISTEN_ONLY, < The CAN controller will not influence the bus (No transmissions or acknowledgments) but can receive messages
*
* CAN_GENERAL_CONFIG_DEFAULT(op_mode) {.mode = op_mode, .tx_io = GPIO_NUM_32, .rx_io = GPIO_NUM_35, \
* .clkout_io = CAN_IO_UNUSED, .bus_off_io = CAN_IO_UNUSED, \
* .tx_queue_len = 5, .rx_queue_len = 5, \
* .alerts_enabled = CAN_ALERT_NONE, .clkout_divider = 0, \
* .intr_flags = ESP_INTR_FLAG_LEVEL1}
*/
can_general_config_t g_config = CAN_GENERAL_CONFIG(CAN_MODE_NORMAL);
/** 这里对can_timing_config_t这个结构体内容进行注释说明,用户可以直接选用使用初始化宏来配置CAN的通信速率
* typedef struct {
* uint32_t brp; < Baudrate prescaler (i.e., APB clock divider) can be any even number from 2 to 128.
* For ESP32 Rev 2 or later, multiples of 4 from 132 to 256 are also supported
* uint8_t tseg_1; < Timing segment 1 (Number of time quanta, between 1 to 16)
* uint8_t tseg_2; < Timing segment 2 (Number of time quanta, 1 to 8)
* uint8_t sjw; < Synchronization Jump Width (Max time quanta jump for synchronize from 1 to 4)
* bool triple_sampling; < Enables triple sampling when the CAN controller samples a bit
* } can_timing_config_t;
*
* CAN_TIMING_CONFIG_25KBITS() {.brp = 128, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false}
* CAN_TIMING_CONFIG_50KBITS() {.brp = 80, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false}
* CAN_TIMING_CONFIG_100KBITS() {.brp = 40, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false}
* CAN_TIMING_CONFIG_125KBITS() {.brp = 32, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false}
* CAN_TIMING_CONFIG_250KBITS() {.brp = 16, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false}
* CAN_TIMING_CONFIG_500KBITS() {.brp = 8, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false}
* CAN_TIMING_CONFIG_800KBITS() {.brp = 4, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false}
* CAN_TIMING_CONFIG_1MBITS() {.brp = 4, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false}
*/
can_timing_config_t t_config = CAN_TIMING_CONFIG_500KBITS();
/** 这里对can_filter_config_t这个结构体内容进行注释说明,用户可以直接选用使用初始化宏来配置接收过滤器
*
* typedef struct {
* uint32_t acceptance_code; < 32-bit acceptance code
* uint32_t acceptance_mask; < 32-bit acceptance mask
* bool single_filter; < Use Single Filter Mode
* } can_filter_config_t;
*
* CAN_FILTER_CONFIG_ACCEPT_ALL() {.acceptance_code = 0, .acceptance_mask = 0xFFFFFFFF, .single_filter = true}
*
*/
can_filter_config_t f_config = CAN_FILTER_CONFIG_ACCEPT_ALL();
while(!ESP32Can.init(&g_config,&t_config,&f_config)){
Serial.println("CAN init err!!!");
delay(1000);
}
while(!ESP32Can.start()){
Serial.println("CAN start err!!!");
delay(1000);
}
ESP32Can.clearTransmitQueue();
tx_message.identifier = 0x0006;
tx_message.data_length_code = 4;
/**flags:
* CAN_MSG_FLAG_NONE < No message flags (Standard Frame Format)
* CAN_MSG_FLAG_EXTD < Extended Frame Format (29bit ID)
* CAN_MSG_FLAG_RTR < Message is a Remote Transmit Request
* CAN_MSG_FLAG_SS < Transmit as a Single Shot Transmission
* CAN_MSG_FLAG_SELF < Transmit as a Self Reception Request
*/
tx_message.flags = CAN_MSG_FLAG_NONE;
tx_message.data[0] = 0;
tx_message.data[1] = 1;
tx_message.data[2] = 2;
tx_message.data[3] = 3;
}
void loop() {
if(ESP32Can.transmit(&tx_message,pdMS_TO_TICKS(1000))){
Serial.println("Message queued for transmission");
} else{
Serial.println("Failed to queue message for transmission");
}
delay(1000);
}
将代码下载到另外一块主板。主板每一秒发送一个数据帧。将主板的 CAN Bus 连接到USB CAN分析器。
USB CAN分析器接收到主板发送的数据如下图所示。
8.5 蓝牙
Edge101主板支持双模蓝牙,即同时支持经典蓝牙和蓝牙低功耗。主机可以与控制器运行在同⼀个宿主上,也可以分布在不同的宿主上。Edge101主板可以支持上述两种方式。
8.5.1 例程:经典蓝牙(BL)
主板蓝牙工作在Bluetooth Classic模式,生成一个蓝牙串口。
硬件准备:ESP32 IoT 可编程控制器(SKU:DFR0886)×1
示例代码:
// 此示例代码属于公共领域(Public Domain)或 CC0 许可证(可选)。
// 作者:Evandro Copercini - 2018
//
// 本示例在串口与经典蓝牙 (SPP) 之间创建了一个桥接,
// 并演示了 SerialBT 具有与普通串口相同的功能。
#include "BluetoothSerial.h"
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif
BluetoothSerial SerialBT;
void setup() {
Serial.begin(115200);
SerialBT.begin("Edge101_Device"); // 设置蓝牙设备名称
Serial.println("The device started, now you can pair it with bluetooth!");
}
void loop() {
// 如果串口有数据,发送到蓝牙
if (Serial.available()) {
SerialBT.write(Serial.read());
}
// 如果蓝牙有数据,发送到串口
if (SerialBT.available()) {
Serial.write(SerialBT.read());
}
delay(20); // 稍作延迟,减少 CPU 占用
}
在安卓系统手机上安装 Serial Bluetooth Terminal 软件。手机打开蓝牙,搜索到 Edge101_Device 这个蓝牙设备连接上后。
打开 Serial Bluetooth Terminal 软件选择 Edge101_Device 设备。
连接后可在界面上发送数据到主板,主板串口将打印出手机发送的数据。同时主板也可以通过串口发送数据到手机。
8.5.2 例程:低功耗蓝牙(BLE)
程序将建立一个 BLE 设备,当按下主板的用户按钮时,将修改 BLE 设备的名字,可通过手机查看 BLE 设备的名字变化。
硬件准备:ESP32 IoT 可编程控制器(SKU:DFR0886)×1
示例代码:
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// 本示例展示如何使用SimpleBLE库来广播设备名称,并通过按下按钮来改变设备名称。
// 如果您想广播某些消息,这是一个有用的示例。
// 按钮连接在GPIO 0和GND之间,每次按下按钮时设备名称都会发生变化。
#include "SimpleBLE.h" // 使用SimpleBLE库
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif
SimpleBLE ble; // 创建SimpleBLE对象
// 按钮按下时调用的函数
void onButton(){
String out = "BLE32 name: "; // 创建一个字符串,用来存储设备名称
out += String(millis() / 1000); // 获取自程序启动以来的秒数并附加到名称上
Serial.println(out); // 打印新设备名称
ble.begin(out); // 设置设备的BLE名称
}
void setup() {
Serial.begin(115200); // 初始化串口,波特率115200
Serial.setDebugOutput(true); // 启用调试输出
pinMode(38, INPUT_PULLUP); // 设置GPIO38为输入模式,并启用内部上拉电阻,用于连接用户按钮
Serial.print("Edge101WE SDK: ");
Serial.println(ESP.getSdkVersion()); // 打印当前使用的SDK版本
ble.begin("Edge101WE SimpleBLE"); // 启动BLE广播,初始设备名称为"Edge101WE SimpleBLE"
Serial.println("Press the button to change the device's name"); // 提示用户按下按钮更改设备名称
}
void loop() {
static uint8_t lastPinState = 1; // 上次按钮状态
uint8_t pinState = digitalRead(38); // 读取GPIO38上的按钮状态
if(!pinState && lastPinState){ // 如果按钮被按下(按钮按下时GPIO状态为低)
onButton(); // 调用onButton函数改变设备名称
}
lastPinState = pinState; // 更新按钮状态
while(Serial.available()) Serial.write(Serial.read()); // 将串口接收的数据传输出去
}
8.6 WiFi
8.6.1 例程:WiFi Scan
WiFi Scan 有同步搜索和异步搜索,这里采用的是同步搜索。缺点是默认在阻塞模式下运行,程序会扫描WiFi,期间什么事情都做不了,可以通过参数设置改为异步模式。
本例程首先将 WiFi 置于 station 工作模式,如果设备处于 AP 工作模式下,且正被其他设备连接,将会断开连接。
然后调用 WiFi.scanNetworks() 扫描WiFi。如果扫描到网络将网络信息打印出来。
硬件准备:ESP32 IoT 可编程控制器(SKU:DFR0886)×1
示例代码:
/*
* 此示例演示如何扫描 WiFi 网络。
* 该 API 与 WiFi Shield 库几乎相同,
* 最明显的区别是需要包含不同的头文件:
*/
#include "WiFi.h"
void setup()
{
Serial.begin(115200);
// 将 WiFi 设置为 station(客户端)模式,如果设备处于 AP(热点)模式并有连接,将会断开连接
WiFi.mode(WIFI_STA); // 设置为 Station 模式
WiFi.disconnect(); // 断开当前 WiFi 连接
delay(100);
Serial.println("Setup done");
}
void loop()
{
Serial.println("scan start");
// WiFi.scanNetworks() 函数会返回已发现的网络数量
int n = WiFi.scanNetworks();
Serial.println("scan done");
if (n == 0) {
Serial.println("no networks found");
} else {
Serial.print(n);
Serial.println("networks found");
for (int i = 0; i < n; ++i) {
// 打印每个网络的 SSID(服务集标识符)和 RSSI(接收信号强度指示)
Serial.print(i + 1);
Serial.print(": ");
Serial.print(WiFi.SSID(i)); // 输出网络名称
Serial.print(" (");
Serial.print(WiFi.RSSI(i)); // 输出信号强度
Serial.print(")");
// 根据加密方式输出 " "(开放网络)或 "*"(加密网络)
// WIFI_AUTH_OPEN 代表没有加密(不需要密码)
Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? " " : "*");
delay(10);
}
}
Serial.println("");
// 等待一段时间后再次扫描
delay(5000);
}
运行结果:从串口打印出扫描到的 WiFi。
8.6.2 例程:Web网页配网
首先WiFi设备处于AP模式,默认IP地址为192.168.4.1。通过网页访问IP连上这个AP,在输入框中输入路由器网络WiFi的SSID和密码,WiFi设备收到后,切换到STA模式,利用收到的信息联网。优点是成功率100%,但是需要一个按钮让设备进入配置模式。
Web 网页配网有如下优点:
- 直接输入,配网简单,过程明了,成功率高。
- 可以配至的路由或热点,不受限制不必连互联网。
- 不需要在系统上添加其他接口,适合封闭或不方便引出额外接口的场合。
- 可以通过任意支持WIFI和浏览器的设备来配网,非常灵活实用。
硬件准备:ESP32 IoT 可编程控制器(SKU:DFR0886)×1
示例代码:
#include <WiFi.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <esp_wifi.h>
const char* AP_SSID = "Edge101_"; //热点名称
String wifi_ssid = "";
String wifi_pass = "";
String scanNetworksID = "";//用于储存扫描到的WiFi
#define ROOT_HTML "<!DOCTYPE html><html><head><title>WIFI Config by DFRobot</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"></head><style type=\"text/css\">.input{display: block; margin-top: 10px;}.input span{width: 100px; float: left; float: left; height: 36px; line-height: 36px;}.input input{height: 30px;width: 200px;}.btn{width: 120px; height: 35px; background-color: #000000; border:0px; color:#ffffff; margin-top:15px; margin-left:100px;}</style><body><form method=\"GET\" action=\"connect\"><label class=\"input\"><span>WiFi SSID</span><input type=\"text\" name=\"ssid\"></label><label class=\"input\"><span>WiFi PASS</span><input type=\"text\" name=\"pass\"></label><input class=\"btn\" type=\"submit\" name=\"submit\" value=\"Submie\"> <p><span> Nearby wifi:</P></form>"
WebServer server(80);
#define RESET_PIN 38 //GPIO 38 User Key用于删除WiFi信息
void setup() {
Serial.begin(115200);
pinMode(RESET_PIN, INPUT_PULLUP);
// 连接WiFi
if (!AutoConfig())
{
wifi_Config();
}
//用于删除已存WiFi
if (digitalRead(RESET_PIN) == LOW) {
Serial.println("Delete WiFi and restart");
delay(1000);
esp_wifi_restore();
delay(10);
ESP.restart(); //复位esp32
}
}
void loop() {
server.handleClient();
while (WiFi.status() == WL_CONNECTED) {
//WIFI已连接
}
}
//用于配置WiFi
void wifi_Config()
{
Serial.println("scan start");
// 扫描附近WiFi
int n = WiFi.scanNetworks();
Serial.println("scan done");
if (n == 0) {
Serial.println("no networks found");
scanNetworksID = "no networks found";
} else {
Serial.print(n);
Serial.println(" networks found");
for (int i = 0; i < n; ++i) {
// Print SSID and RSSI for each network found
Serial.print(i + 1);
Serial.print(": ");
Serial.print(WiFi.SSID(i));
Serial.print(" (");
Serial.print(WiFi.RSSI(i));
Serial.print(")");
Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? " " : "*");
scanNetworksID += "<P>" + WiFi.SSID(i) + "</P>";
delay(10);
}
}
Serial.println("");
WiFi.mode(WIFI_AP);//配置为AP模式
boolean result = WiFi.softAP(AP_SSID, ""); //开启WIFI热点
if (result)
{
IPAddress myIP = WiFi.softAPIP();
//打印相关信息
Serial.println("");
Serial.print("Soft-AP IP address = ");
Serial.println(myIP);
Serial.println(String("MAC address = ") + WiFi.softAPmacAddress().c_str());
Serial.println("waiting ...");
} else { //开启热点失败
Serial.println("WiFiAP Failed");
delay(3000);
ESP.restart(); //复位esp32
}
if (MDNS.begin("esp32")) {
Serial.println("MDNS responder started");
}
//首页
server.on("/", []() {
server.send(200, "text/html", ROOT_HTML + scanNetworksID + "</body></html>");
});
//连接
server.on("/connect", []() {
server.send(200, "text/html", "<html><body><font size=\"10\">successd,wifi connecting...<br />Please close this page manually.</font></body></html>");
WiFi.softAPdisconnect(true);
//获取输入的WIFI账户和密码
wifi_ssid = server.arg("ssid");
wifi_pass = server.arg("pass");
server.close();
WiFi.softAPdisconnect();
Serial.println("WiFi Connect SSID:" + wifi_ssid + " PASS:" + wifi_pass);
//设置为STA模式并连接WIFI
WiFi.mode(WIFI_STA);
WiFi.begin(wifi_ssid.c_str(), wifi_pass.c_str());
uint8_t Connect_time = 0; //用于连接计时,如果长时间连接不成功,复位设备
while (WiFi.status() != WL_CONNECTED) { //等待WIFI连接成功
delay(500);
Serial.print(".");
Connect_time ++;
if (Connect_time > 80) { //长时间连接不上,复位设备
Serial.println("Connection timeout, check input is correct or try again later!");
delay(3000);
ESP.restart();
}
}
Serial.println("");
Serial.println("WIFI Config Success");
Serial.printf("SSID:%s", WiFi.SSID().c_str());
Serial.print(" LocalIP:");
Serial.print(WiFi.localIP());
Serial.println("");
});
server.begin();
}
//用于上电自动连接WiFi
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;
}
8.7 以太网
例程:以太网与WiFi的切换
硬件准备:ESP32 IoT 可编程控制器(SKU:DFR0886)×1
示例代码:需要修改WiFi的SSID和密码,然后将程序烧录到主板。
/*
以太网与WiFi网络切换控制
默认上电时使用以太网模式:
- 检测到以太网连接超时后切换至WiFi模式
- 在WiFi模式下检测到以太网恢复时自动切换回以太网模式
*/
#include <ETH.h>
#include <WiFi.h>
const char* ssid = "yourssid"; // WiFi网络名称
const char* password = "yourpasswd"; // WiFi密码
// 网络状态标志
static bool eth_connected = false; // 以太网连接状态
bool wifi_mode = false; // WiFi模式标志
bool eth_mode = true; // 以太网模式标志
uint64_t time_start = 0 ; // 以太网启动时间戳
uint64_t time_connected = 0 ; // 以太网连接成功时间戳
// WiFi事件回调函数
void WiFiEvent(WiFiEvent_t event)
{
switch (event) {
case ARDUINO_EVENT_ETH_START: // 以太网启动事件
ETH.setHostname("esp32-ethernet");// 设置设备主机名
time_start = millis(); // 记录启动时间
break;
case ARDUINO_EVENT_ETH_CONNECTED: // 物理层连接建立
time_connected = millis();
Serial.println("ETH Connected");
if(wifi_mode){ // 如果当前是WiFi模式则重启
ESP.restart();
}
break;
case ARDUINO_EVENT_ETH_GOT_IP: // 成功获取IP地址
Serial.print("ETH MAC: ");
Serial.print(ETH.macAddress()); // 输出MAC地址
Serial.print(", IPv4: ");
Serial.print(ETH.localIP()); // 输出IP地址
if (ETH.fullDuplex()) { // 全双工状态检测
Serial.print(", FULL_DUPLEX");
}
Serial.print(", ");
Serial.print(ETH.linkSpeed()); // 输出连接速率
Serial.println("Mbps");
eth_connected = true; // 更新连接状态
break;
case ARDUINO_EVENT_ETH_DISCONNECTED:// 物理连接断开
Serial.println("ETH Disconnected");
if(!wifi_mode){ // 非WiFi模式时尝试重启
ESP.restart();
}
eth_connected = false;
eth_mode = false;
break;
case ARDUINO_EVENT_ETH_STOP: // 以太网停止
Serial.println("ETH Stopped");
eth_connected = false;
break;
default:
break;
}
}
// 网络连接测试函数
void testClient(const char * host, uint16_t port)
{
Serial.print("\nconnecting to ");
Serial.println(host); // 输出目标主机信息
WiFiClient client;
// 尝试建立TCP连接
if (!client.connect(host, port)) {
Serial.println("connection failed");
return;
}
// 发送HTTP请求
client.printf("GET / HTTP/1.1\r\nHost: %s\r\n\r\n", host);
// 等待服务器响应
while (client.connected() && !client.available());
// 读取返回数据
while (client.available()) {
Serial.write(client.read());
}
Serial.println("closing connection\n");
client.stop();
}
void setup()
{
Serial.begin(115200); // 初始化串口
WiFi.onEvent(WiFiEvent); // 注册网络事件回调
ETH.begin(); // 启动以太网
delay(5000); // 等待5秒初始化
// 以太网连接超时检测(4秒未连接)
if((millis()-time_start>4000)&&(time_connected==0)){
// 启动WiFi连接
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
wifi_mode = true; // 进入WiFi模式
}
}
void loop()
{
// 在有效连接模式下测试网络
if (eth_connected||wifi_mode) {
testClient("baidu.com", 80); // 测试百度服务器连接
}
delay(10000); // 10秒检测周期
}
接下来将将网线连接到 Edge101主板的 以太网接口上,网口的绿色LED亮代表连接好,橙色LED闪烁代表在通信。
此时串口打印以太网连接成功、以太网的MAC地址、IP地址等信息。然后程序每10秒访问一次 www.baidu.com 并返回获取的数据。
如果拔下网线,此时显示以太网断开,主板重启,然后通过WiFi方式去联网并访问网址。
8.8 4G
Edge101主板出厂不带4G模块,但支持安装4G模组。
例程:获取网络时间
硬件准备:ESP32 IoT 可编程控制器(SKU:DFR0886)×1
示例代码:
/*!
* @file setNTP.ino
* @brief : 网络时间同步示例
* @copyright Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com)
* @licence The MIT License (MIT)
* @author [yangfeng]<feng.yang@dfrobot.com>
* @version V1.0
* @date 2021-08-18
*/
#include <DFRobot_AIR720UH.h>
DFRobot_AIR720UH AIR720UH;
void setup(){
int signalStrength,dataNum;
Serial.begin(115200);
Serial1.begin(115200);
while(!Serial);
AIR720UH.begin(Serial1);
// SIM卡状态检测
Serial.println("Check SIM card......");
if(AIR720UH.checkSIMStatus()){ // 检测SIM卡状态
Serial.println("SIM card READY");
}else{
Serial.println("SIM card ERROR, Check if you have insert SIM card and restart AIR720UH");
while(1);
}
// 信号质量检测
Serial.println("Get signal quality......");
delay(500);
signalStrength=AIR720UH.checkSignalQuality(); // 获取信号强度值
Serial.print("signalStrength =");
Serial.println(signalStrength);
// NTP网络校时配置
delay(500);
Serial.println("set NTP ...");
while(1){
int data=AIR720UH.setNTP();
if(data==1){
Serial.println("The network time is synchronized successfully!"); // 校时成功
break;
}else if(data ==61){
Serial.println("Network error"); // 网络错误
break;
}else if(data ==62){
Serial.println("DNS resolution error"); // DNS解析失败
break;
}else if(data ==63){
Serial.println("Connection error"); // 连接异常
break;
}else if(data ==64){
Serial.println("Service response error"); // 服务响应异常
break;
}else if(data ==65){
Serial.println("Service response time out"); // 服务响应超时
break;
}else{
Serial.println("set error"); // 未知错误
}
}
}
void loop(){
// 持续获取并显示网络时间
char *buff = AIR720UH.getCLK();
if(buff!=NULL){
Serial.println(buff); // 输出格式示例:2021/08/18 14:30:45
}
}
8.9 应用层协议
例程:MQTT——发布与订阅
连接一个公共的 MQTT 服务器,每2秒发布一次 “hello world” 消息到主题 “outTopic”,客户端监听主题 “inTopic”,并判断负载内容来控制灯亮灭。
硬件准备:ESP32 IoT 可编程控制器(SKU:DFR0886)×1
示例代码:将下方的代码里WiFi的SSID和密码修改为你自己的WiFi SSID和密码,然后将程序上传到主板。
#include <WiFi.h>
#include <PubSubClient.h>
#define LED 15 // LED控制引脚(根据实际硬件连接调整)
/* 网络配置参数(需用户修改)*/
const char* ssid = "your_ssid"; // WiFi网络名称
const char* password = "your_password"; // WiFi密码
const char* mqtt_server = "broker.mqtt-dashboard.com"; // MQTT服务器地址
WiFiClient espClient; // WiFi客户端实例
PubSubClient client(espClient); // MQTT客户端实例
long lastMsg = 0; // 最后消息时间戳
char msg[50]; // 消息缓冲区
int value = 0; // 测试计数器
/* WiFi连接初始化 */
void setup_wifi() {
delay(10);
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password); // 启动WiFi连接
// 等待连接成功
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
randomSeed(micros()); // 初始化随机数种子
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP()); // 显示获取的IP地址
}
/* MQTT消息回调函数 */
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic); // 显示消息主题
Serial.print("] ");
// 打印消息内容
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
// 根据消息首字符控制LED
if ((char)payload[0] == '0') {
digitalWrite(LED, LOW); // 收到'0'关闭LED(注意低电平有效)
} else {
digitalWrite(LED, HIGH); // 收到非'0'开启LED
}
}
/* MQTT重连机制 */
void reconnect() {
while (!client.connected()) { // 持续尝试连接
Serial.print("Attempting MQTT connection...");
// 生成随机客户端ID(避免重复)
String clientId = "FireBeetleClient-";
clientId += String(random(0xffff), HEX);
if (client.connect(clientId.c_str())) { // 连接尝试
Serial.println("connected");
client.publish("outTopic", "hello world"); // 发布初始消息
client.subscribe("inTopic"); // 订阅输入主题
} else {
Serial.print("failed, rc="); // 显示错误状态码
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}
/* 初始化设置 */
void setup() {
pinMode(LED, OUTPUT); // 初始化LED引脚为输出模式
Serial.begin(115200); // 启动串口通信
setup_wifi(); // 连接WiFi网络
client.setServer(mqtt_server, 1883); // 配置MQTT服务器
client.setCallback(callback); // 设置消息回调函数
}
/* 主循环 */
void loop() {
if (!client.connected()) { // 维持MQTT连接
reconnect();
}
client.loop(); // 处理MQTT消息
// 每2秒发布测试消息
long now = millis();
if (now - lastMsg > 2000) {
lastMsg = now;
++value;
snprintf(msg, 75, "hello world #%ld", value); // 生成消息内容
Serial.print("Publish message: ");
Serial.println(msg);
client.publish("outTopic", msg); // 发布到指定主题
}
}
运行结果:串口打印结果如下。
9. 产品尺寸图
单位:mm
前视尺寸图
后视尺寸图
顶视尺寸图
侧视尺寸图
10. 资料下载
DFR0886_3D.STP
DFR0886_2D.pdf
11. 更多
还没有客户对此产品有任何问题,欢迎通过qq或者论坛联系我们!
更多问题及有趣的应用,可以 访问论坛 进行查阅或发帖。