这篇文章来源于deviceplus.com英语网站的翻译稿。
本文最初发布在deviceplus.jp网站上,而后被翻译成英语。
目录
前言
关于esp32闹钟
创建日期和时间的“rtc”
在esp32 lcd上显示日期和时间
使用mp3模块发出闹铃响声
给闹钟主机接线
给esp32闹钟分机接线
安装库和字体文件
arduino ide闹钟程序
分机程序
确认运行情况
结论
相关文章
前言
您每天早上被什么闹钟吵醒?早上人们的一个常见问题是关掉闹钟并直接回去睡觉。
这一次,我们决定使用esp32制作一个“唤醒闹钟”,应该有助于解决这个问题。
设计步骤
预计完成时间:120分钟
名称 卖方 价格
esp32-devkitc(2 件) 贸泽电子 约10.00美元
ds3231 模块 亚马逊 约3.00美元
lir2032(纽扣充电电池) 亚马逊 约3.60美元
扬声器 亚马逊 约1.00~3.00美元
2.8英寸spi连接器 320×240像素lcd屏幕 亚马逊 约15.00美元
*除上述物品外,还需要一个轻触开关、一个led和一个约100ω的电阻器。
关于esp32闹钟
我们来使用esp32完成一个目标吧:像普通闹钟一样在屏幕上显示日期和时间,并在指定的时间响起。在设计制作这款“唤醒闹钟”时,我们需要将分机按钮(用于关闭闹钟)与闹钟主机分开放置。之所以这样设计,是因为想要您必须起床并走到分机按钮处才能将闹钟关掉。
如下面的视频所示,使用两个esp,一个用于主机,另一个用于分机。在视频中,主机和分机是挨着的,但如果通过wi-fi连接,它们是可以在wi-fi范围内分开放置的。
将主机和分机分开放置时,请使用esp32的wi-fi功能。两个esp32作为web服务器和客户端运行,并相互通信,如图1所示。
图 1 主机和分机之间的通信
创建日期和时间的“rtc”
对于arduino等微控制器来说,通常能够获取在启动程序后经过的时间。但是,您获得的时间通常不是很准确,因为断电时会重置经过时间。
而如果使用esp32,则可以通过wi-fi连接到互联网,以定期从互联网上的ntp服务器获取日期和时间,并将其设置到esp32上。但arduino uno等部分微控制器没有互联网连接功能,因此,拥有一种可以更轻松地处理日期和时间的机制会很方便。
这就是为什么经常使用一种被称作“rtc”(实时时钟)的ic。 rtc是基于周期性发出信号的元件来计时的ic。此外,通过将其连接到电池等外部电源,即使在微控制器断电时也可以继续计时。
许多产品都采用rtc,此次,我们将使用一种名为“ds3231”的rtc模块。
在电子设计所用的rtc模块当中,ds3231模块很受欢迎,且很容易获得。由于接口是i2c,因此只需要4根线。除了rtc功能,还具有温度传感器功能(不过本文不会用到温度传感器)。
此外,在照片1所示的ds3231上,安装一个名为“lir2032”的纽扣电池,这样即使在微控制器关闭的情况下也能继续记录日期和时间。lir2032的电池尺寸与cr2032的相同,但不同的是它可充电。
照片1 ds3231模块
在esp32 lcd上显示日期和时间
由于闹钟用于查看当前日期和时间,因此需要以易于理解的方式显示日期和时间。以下设备用于显示日期和时间。
7段led
led 矩阵
oled 显示器
字符液晶显示器
图形液晶显示器
根据设备的不同,有不同的库和不同的编程方法。这还取决于它是否适合您想要制作的作品。例如,7段led仅适合以低成本显示数字,但不适合显示详情。其中,图形液晶显示器是最通用的,可以用于许多不同的项目,所以我这次决定使用它。
市面上有各种类型的液晶显示器模块,但对于今天的项目,我们将使用一个名为“ili9341”的控制器,并使用spi接口(照片2)。此外,液晶显示器通常以2.2英寸/2.4英寸/2.8英寸尺寸出售,因此,请根据您正在做的作品类型进行相应调整。
照片2 控制器用ili9341 2.8英寸液晶显示器
使用mp3模块发出闹钟铃声
既然是闹钟,在指定时间发出铃声是必需的。您可以将蜂鸣器连接到esp32以产生单一铃声,但如果您愿意,也可以使用自己喜欢的铃声。为此,我们将使用一个名为“dfplayer mini”的模块,它可以播放任何mp3数据(照片3)。
dfplayer mini是一个可以通过串口发送命令来播放microsd卡中mp3的模块。可以将一个小型扬声器连接到扬声器输出引脚以产生铃声。
照片3 dfplayer mini
给闹钟主机接线
让我们进入实际构建吧。首先,给闹钟接线。
使用两个面包板,一个配有esp32和ds3231,另一个配有液晶显示器(lcd)和dfplayer mini。各部件接线如图2所示。
由于esp32很宽,您只能在普通面包板的一面放一根跳线。因此,请改用电源线只在一侧、多一排插孔的面包板(例如sanhayato的sad-101)。
esp32和液晶显示器通过spi进行连接。esp32可以使用两个 spi(vspi和hspi),但使用的是vspi(引脚18/19/23)(表1)。
esp32和ds3231通过i2c进行连接。在esp32中,i2c可以分配给任何引脚,但我们使用标准引脚(sda=21和scl=22)(表 2)。
dfplayer mini 进行串行连接。esp32可以使用3个串口,但为此请同时使用引脚16和引脚17(表3)。此外,将扬声器连接到dfplayer mini“spk1”和“spk2”两个引脚。
图2 闹钟主机接线
esp32 引脚 液晶显示器引脚
5v vcc
gnd gnd
5 cs
4 reset
2 dc
23 mosi
18 sck
19 miso
表 1:esp32 与液晶显示器的连接
esp32 引脚 ds3231 引脚
5v vcc
gnd gnd
21 sda
22 scl
表 2:esp32与ds3231的连接
esp32 引脚 dfplayer mini 引脚
5v vcc
gnd gnd
16 tx
17 rx
表 3:esp32与dfplayer mini的连接
给esp32闹钟分机接线
接下来,我们将给分机接线。分机接线应按图3所示进行。您所要做的就是将开关和 led连接到esp32。将开关的一侧连接到esp32的3v3引脚,另一侧连接到引脚4。通过电阻器→led再通过esp32的引脚13连接到gnd。
在读取开关状态的电路上插入一个上拉电阻或下拉电阻。但是,由于esp32可以通过内部电阻进行上拉/下拉,因此省略了外部电阻。
图3 分机接线
安装库和字体文件
完成接线工作后,您可以创建程序。首先,从安装下面的各个库开始。
adafruit gfx
adafruit ili9341
rtclib
dfrobotdfplayermini
安装步骤如下:
启动arduino ide。
选择“sketch”->“include library”->“manage library”菜单,以打开library manager。
在“filter search”字段中输入“adafruit gfx”。
adafruit gfx将在库列表中显示。单击“install”按钮(图 4)。
以相同的方式安装每个库。
有几个名称相似的rtclib和dfplayer库。rtclib 安装“rtclib by adafruit”,而dfplayer安装“dfrobotdfplayermini by dfrobot”。
图 4 adafruit gfx库安装
此外,为通过大字符显示时间,需要安装字体文件。如果您下载并解压缩以下zip文件,将可以获得一个名为“freesans40pt7b.h”的文件。
打开arduino ide的标准草图目标文件夹,再打开“libraries”->“adafruit_gfx_library”->“fonts”文件夹,将字体文件复制到那里。
https://www.h-fj.com/deviceplus/font.zip
arduino ide闹钟程序
接下来,在arduino ide中创建一个闹钟程序并将其写入esp32。程序内容如清单1所示。
清单1:闹钟主机程序
程序内容(放在这里)
但是,第17行到第21行需要改写如下:
・第17/18行
根据您的wi-fi路由器的ssid/密码重写。
・第19行
指定要分配给ess32的ip地址。根据您的wi-fi路由器的网络配置自行决定ip地址。
在普通ip地址中,四组数字用句点分隔,但在这一行中,它是函数参数的形式,所以四组数字用逗号分隔。
・第20行
指定网络默认网关的ip地址。通常,它是wi-fi路由器的ip地址。用逗号分隔ip地址中的四组数字。
・第21行
根据分配给分机esp32的ip地址重写。
例如,如果您想按照表4所示进行设置,请重写第17行至第21行,如清单2所示。
项目 设定值
wi-fi路由器ssid my_wifi
wi-fi路由器密码 my_password
分配给主机esp32的ip地址 192.168.1.101
默认网关ip地址 192.168.1.1
分配给分机esp32的ip地址 192.168.1.102
表4:主机网络设置示例
清单 2:重写第17-21行的示例
分机程序
分机程序如清单3所示。
以与闹钟主机相同的方式重写第5行到第9行。 但是,在第7行,指定分配给分机的ip地址。 此外,在第9行的“main console ip address(主控台ip地址)”中指定闹钟的ip地址。
清单 3:分机程序
程序内容
#include#include#include#include #include #include #include#include#include time.h#include#include#include#include#include
// initial setup
const char *ssid = “wi-fi ssid”;
const char *pass = “wi-fi password”;
ipaddress ip(ip address assigned to main unit);
ipaddress gateway(ip address of default gateway);
const char* notify_url = “http://ip address of extension unit/alarm”;
const char* adjust_time = “04:00:00”;
#define df_volume 30
// constants, etc.
#define alarm_sig 25
#define tft_dc 2
#define tft_cs 5
#define tft_rst 4
#define tft_width 320
adafruit_ili9341 tft = adafruit_ili9341(tft_cs, tft_dc, tft_rst);
rtc_ds3231 rtc;
hardwareserial hs(1);
dfrobotdfplayermini mydfplayer;
webserver server(80);
char old_date[15];
char old_time[9];
char old_alarm[15];
char alarm_time[9];
char wdays[7][4] = { “sun”, “mon”, “tue”, “wed”, “thu”, “fri”, “sat” };
bool alarm_checked = false;
bool alarm_on = false;
bool ntp_adjusted = false;
int alarm_ctr;
// set date and time using ntp
void settimebyntp() {
struct tm t;
configtime(9 * 3600l, 0, “ntp.nict.jp”, “time.google.com”, “ntp.jst.mfeed.ad.jp”);
if (!getlocaltime(&t)) {
serial.println(“getlocaltime error”);
return;
}
rtc.adjust(datetime(t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec));
}
// display string on lcd
void showmessage(char* s_new, char* s_old, int y0, int height) {
int16_t x1, y1;
uint16_t w, w2, h;
int x, y;
if (strcmp(s_new, s_old) != 0) {
tft.gettextbounds(s_old, 0, 0, &x1, &y1, &w, &h);
w2 = w * 11 / 10;
tft.fillrect((tft_width – w2) / 2 , y0 – (height / 2) + 1, w2, height, ili9341_black),
tft.gettextbounds(s_new, 0, 0, &x1, &y1, &w, &h);
tft.setcursor((tft_width – w) / 2, y0 + (h / 2) – 1);
tft.print(s_new);
strcpy(s_old, s_new);
}
}
// main settings page
void handleroot() {
int i;
string html =
“n”
“
n”
“n”
“ n”
“ n”
“n”
“n”
“ n”
“
n”
“n”; for (i = 0; i ”; html += string(i); html += “n”; } html += “(h)n”;
html += “n”; for (i = 0; i ”; html += string(i); html += “n”; } html += “(min) n”;
html += “n”;
html += “
n”;
html += “ n”;
html += “ n”;
html += “n”;
html += “
n”;
html += “ n”;
html += “n”;
html += “n”;
server.send(200, “text/html”, html);
}
// set alarm
void handlesetalarm() {
int i, hour, min, sec;
bool is_off = false;
string s_hour = “”, s_min = “”, s_sec = “”;
// get “off/hour/min/sec” parameters from url
for (i = 0; i 0 && s_min.length() > 0) {
hour = s_hour.toint();
min = s_min.toint();
if (s_sec.length() > 0) {
sec = s_sec.toint();
}
else {
sec = 0;
}
if (hour >= 0 && hour = 0 && min = 0 && sec <= 59) {
sprintf(alarm_time, “%02d:%02d:%02d”, hour, min, sec);
string msg = “alarm set to “;
msg.concat(alarm_time);
msg.concat(” .”);
server.send(200, “text/plain; charset=utf-8”, msg);
}
else {
server.send(200, “text/plain; charset=utf-8”, “incorrect date/time.”);
}
}
else {
server.send(200, “text/plain; charset=utf-8”, “incorrect parameters.”);
}
}
/ stop alarm
void handlestopalarm() {
mydfplayer.pause();
alarm_on = false;
tft.drawrect(30, 180, 260, 40, ili9341_black);
server.send(200, “text/plain”, “alarm stop”);
}
// if an invalid url is specified
void handlenotfound() {
string message = “not found : “;
message += server.uri();
server.send(404, “text/plain”, message);
}
// setup
void setup() {
int16_t x1, y1;
uint16_t w, h;
serial.begin(115200);
strcpy(old_date, “00000000000000”);
strcpy(old_time, “00000000”);
strcpy(old_alarm, “00000000000000”);
strcpy(alarm_time, “off”);
// initialize display
tft.begin();
tft.setrotation(3);
tft.fillscreen(ili9341_black);
tft.settextcolor(ili9341_white);
tft.setfont(&freesans12pt7b);
string s = “initializing…”;
tft.gettextbounds(s, 0, 0, &x1, &y1, &w, &h);
tft.setcursor(0, h);
tft.println(s);
// connect to wifi
wifi.begin(ssid, pass);
while (wifi.status() != wl_connected) {
delay(500);
serial.print(“.”);
}
wifi.config(ip, gateway, wifi.subnetmask(), ipaddress(8, 8, 8, 8), ipaddress(8, 8, 4, 4));
serial.println(“”);
serial.println(“wifi connected.”);
tft.println(“wifi connected.”);
// initialize dfplayer
hs.begin(9600, serial_8n1, 16, 17);
int count = 0;
while (count < 10) {
if (!mydfplayer.begin(hs)) {
count++;
serial.print(“dfplayer initialize attempt “);
serial.println(count);
}
else {
break;
}
}
if (count < 10) {
serial.println(“dfplayer initialized.”);
tft.println(“dfplayer initialized.”);
mydfplayer.pause();
mydfplayer.volume(df_volume);
}
else {
serial.println(“dfplayer error.”);
tft.println(“dfplayer error.”);
while(1);
}
// initialize rtc
if (!rtc.begin()) {
serial.println(“couldn’t find rtc”);
while (1);
}
serial.println(“rtc initialized”);
tft.println(“rtc initialized.”);
// get current date/time via ntp and set to rtc
settimebyntp();
// initialize web server
server.on(“/”, handleroot);
server.on(“/set”, handlesetalarm);
server.on(“/stop”, handlestopalarm);
server.onnotfound(handlenotfound);
server.begin();
// fill display with black
tft.fillscreen(ili9341_black);
}
void loop() {
char new_time[9], new_date[15], new_alarm[15];
// launch web server
server.handleclient();
// display current date/time on lcd
datetime now = rtc.now();
sprintf(new_date, “%04d/%02d/%02d “, now.year(), now.month(), now.day());
strcat(new_date, wdays[now.dayoftheweek()]);
sprintf(new_time, “%02d:%02d:%02d”, now.hour(), now.minute(), now.second());
strcpy(new_alarm, “alarm “);
strcat(new_alarm, alarm_time);
tft.setfont(&freesans18pt7b);
tft.settextcolor(ili9341_white);
showmessage(new_date, old_date, 40, 28);
showmessage(new_alarm, old_alarm, 200, 28);
tft.setfont(&freesans40pt7b);
showmessage(new_time, old_time, 120, 64);
// check if current time is time set for alarm
if (strstr(new_time, alarm_time) != null) {
if (!alarm_checked) {
// if it’s alarm time, ring out then send message to extension unit
mydfplayer.loop(1);
alarm_checked = true;
alarm_on = true;
alarm_ctr = 0;
httpclient http;
http.begin(notify_url);
int httpcode = http.get();
}
}
else {
alarm_checked = false;
}
// while alarm is sounding, make red frame flash around alarm time on display
if (alarm_on) {
if (alarm_ctr == 0) {
通用电阻器的原理及其分类
电磁阀的识别方法及应用电路
软通动力发布票据管理系统IDMS 助力企业票据数字化向深层次演进
1us电流环!先楫HPM6000系列芯片实现高效电机控制
半导体可靠性测试项目有哪些
用ESP32和RTC做一个“唤醒闹钟”吧!
有源功率因数校正技术及发展趋势
人类再次发现引力波 来自黑洞碰撞
基于ARM和CPLD的温度控制器的设计
华为鸿蒙系统蓄势待发 安卓系统必首先受到冲击
探究十年后容易被智能机器人淘汰的职业
破5.8GHz AMD Ryzen处理器挑战超频极限
电气元器件损坏率高的原因是什么
全球智能手机存储芯片市场三星占据份额47%
高速PCB布局设计技巧
京东方为华硕独家提供17.3英寸可折叠OLED屏
DeepMind提出了可微分归纳逻辑编程方法∂ILP并进行解读
黑鲨游戏手机2怎么样 值不值得买
空气产品公司中国区总裁被任命为陕西省政府国际高级经济顾问
电源模块是什么?电源模块噪音过大的原因及解决办法