最近更新于 2024-05-05 12:31
1 前言
ESP32 买了快三个月了,当时用 Arduino IDE 配置好了开发环境,之后就开始吃灰。最近想起来,打算做个可以联网对时的时钟,所以开始学习探索了。
2 测试环境
2.1 硬件
- ESP32-WROOM-32(只支持 2.4GHz 频段 WiFi)
2.2 软件
- Arduino IDE 2.2.1
- esp32 2.0.14(开发板)
- NTP 1.7.0(NTP 时间同步):https://github.com/sstaub/NTP
3 探索
3.1 WiFi 连接
/**
* WiFi 连接程序
* 启动后自动尝试连接设置的 WiFi
* 如果连接失败,最多尝试 20 次
* 连接成功后可以短接 GND 和 D15 引脚使 WiFi 连接断开
*/
#include <WiFi.h>
const char* ssid = "IYATT-yx_2.4GHz"; // WiFi 名
const char* password = "esp32test"; // 密码
int pin = 15; // D15 引脚用于控制断开 WiFi
int state = false; // 引脚状态
void setup()
{
Serial.begin(115200);
delay(500);
pinMode(pin, INPUT_PULLUP);
Serial.println();
Serial.print("[WiFi] 尝试连接到 ");
Serial.println(ssid);
WiFi.begin(ssid, password);
// 失败尝试重连
int try_delay = 1000; // 尝试间隔延时
int number_of_tries = 20; // 尝试次数
while (true)
{
// 检查 WiFi 连接状态
switch(WiFi.status())
{
case WL_NO_SSID_AVAIL:
{
Serial.println("[WiFi] 没有找到 SSID");
break;
}
case WL_CONNECT_FAILED:
{
Serial.print("[WiFi] 失败 - WiFi 未连接! 原因:");
return;
}
case WL_CONNECTION_LOST:
{
Serial.println("[WiFi] 连接丢失");
break;
}
case WL_SCAN_COMPLETED:
{
Serial.println("[WiFi] 扫描已完成");
break;
}
case WL_CONNECTED:
{
Serial.println("[WiFi] 已连接!");
Serial.print("[WiFi] IP 地址:");
Serial.println(WiFi.localIP());
return;
}
case WL_DISCONNECTED:
{
Serial.println("[WiFi] 连接已断开");
break;
}
default:
{
Serial.print("[WiFi] WiFi 状态:");
Serial.println(WiFi.status());
break;
}
}
delay(try_delay);
if(number_of_tries <= 0)
{
Serial.print("[WiFi] 连接 WiFi 失败!");
WiFi.disconnect();
return;
}
else
{
--number_of_tries;
}
}
}
void loop()
{
state = digitalRead(pin);
if (state == LOW)
{
Serial.println("[WiFi] 正在断开连接!");
if(WiFi.disconnect(true, false))
{
Serial.println("[WiFi] 已断开连接");
}
delay(1000);
}
}
3.2 GET 请求
这里方便测试我就使用电脑上的 Python 运行 http 模块作为服务器端,下面是一个简易的 html 网页
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>hello</title>
</head>
<body>
<h1>你好,世界!</h1>
</body>
</html>
命名保存为 index.html 文件
然后在这个文件所在的路径运行 http 服务(8080 端口)
python -m http.server 8080
同一局域网下的手机可以访问这个网页(电脑IP:8080 ),服务器这边就没啥问题了
然后是 ESP32 的代码
/**
* GET 请求实现
*/
#include <WiFi.h>
#include <WiFiMulti.h>
WiFiMulti WiFiMulti; // 创建一个多 WiFi 实例,可以连接多个 WiFi
void setup()
{
Serial.begin(115200);
delay(10);
WiFiMulti.addAP("IYATT-yx_2.4GHz", "esp32test"); // 首先连接到一个 WiFi,分别是名字和密码
Serial.println();
Serial.println();
Serial.print("等待 WiFi 连接...... ");
while(WiFiMulti.run() != WL_CONNECTED) // 连接失败时重试
{
Serial.print(".");
delay(1000);
}
Serial.println("");
Serial.println("WiFi 已连接");
Serial.println("IP 地址: ");
Serial.println(WiFi.localIP());
delay(500);
}
void loop()
{
const char * host = "192.168.1.249"; // 服务器地址
const uint16_t port = 8080; // 服务器端口
Serial.print("正在连接到 ");
Serial.println(host);
WiFiClient client; // 用于创建 TCP 连接
if (!client.connect(host, port)) // 连接服务器
{
Serial.println("连接失败!");
Serial.println("5 秒后重试......");
delay(5000);
return;
}
client.print("GET /index.html HTTP/1.1\n\n"); // 发送 GET 请求,获取 index.html 文件
int maxloops = 0;
// 等待服务器返回数据
while (!client.available() && maxloops < 1000)
{
++maxloops;
delay(1);
}
if (client.available() > 0) // 如果有数据返回
{
String response = client.readString();
Serial.println("服务器返回的内容:");
Serial.println(response);
}
else
{
Serial.println("连接超时");
}
Serial.println("正在关闭连接。");
client.stop();
Serial.println("等待 5 秒后重试......");
delay(5000);
}
运行后可以看到串口输出了请求到的内容
Python 这边也会显示 ESP32 的请求记录
3.3 NTP 时间同步
3.3.1 第三方库
源码
#include <WiFi.h>
#include <NTP.h>
const char * ssid = "IYATT-yx_2.4GHz";
const char * password = "esp32test";
WiFiUDP wifi_udp;
NTP ntp(wifi_udp);
void setup()
{
const char * ntp_server = "ntp.aliyun.com"; // NTP 服务器,默认的感觉应该不如国内的稳定
Serial.begin(115200);
delay(100);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
Serial.println("正在尝试连接 WiFi ......");
delay(1000);
}
Serial.println("WiFi 已连接");
ntp.isDST(false); // 禁止夏令时
ntp.timeZone(8); // 北京时间
ntp.begin(ntp_server);
Serial.println("开始同步时间:");
}
void loop()
{
ntp.update();
Serial.println(ntp.formattedTime("%Y.%m.%d %A %T")); // 格式化规则同 strftime
delay(1000);
}
3.3.2 官方 API
前面探索 NTP 时间同步的时候不知道 ESP32 内部有系统时间,也不知道官方有提供时间同步功能。本来的计划是 ESP32 还要外接一个实时时钟模块,结果在查阅资料的时候发现官方文档说内部有系统时间,那就大大地简化了。不过因为没有独立电源供电,一旦外接电源断开时钟就会丢失,而实时时钟模块自身是可以使用外部电池供电维持走时。
#include <WiFi.h>
void setup()
{
const char * ssid = "IYATT-yx_2.4GHz";
const char * password = "esp32test";
const char * ntp_server = "ntp.aliyun.com"; // NTP 服务器
const int gmt_offset = 8 * 60 * 60; // UTC/GMT+8,东 8 时区偏移量,单位秒
const int daylight_offset = 0; // 夏令时偏移值,单位秒
Serial.begin(115200);
delay(1000);
// 设置时区
setenv("TZ", "CST-8", 1); // TZ 是时区环境变量,CST-8 是中国标准时间,1 表示覆盖旧值
tzset(); // 作用时区变量
// 连接 WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
Serial.println("正在尝试连接 WiFi ......");
delay(1000);
}
Serial.println("WiFi 已连接");
delay(1000);
// 同步时间
configTime(gmt_offset, daylight_offset, ntp_server);
Serial.println("完成时间同步");
delay(2000);
}
void loop()
{
time_t now = 0;
struct tm time_info;
char strftime_buf[64] = {'\0'};
time(&now);
localtime_r(&now, &time_info);
strftime(strftime_buf, sizeof(strftime_buf), "%Y.%m.%d %A %T", &time_info);
Serial.println(strftime_buf);
delay(1000);
}
ESP32 WiFi 开发