Originally published by Dec 3, 2019
This article was translated to English, and was originally published for deviceplus.jp.
What alarm do you wake up to every morning? A common problem among morning folk is stopping the alarm and going straight back to sleep.
This time, we decided to use ESP32 to make a “wake up alarm clock” that should help solve this problem.
Est. time:120 min
Name | Seller | Price |
ESP32-DevKitC(2 pc) | Mouser | About $10.00 |
DS3231 Module | Amazon | About $3.00 |
LIR2032 (Button charger) | Amazon | About $3.60 |
Speaker | Amazon | About $1.00~$3.00 |
2.8-inch SPI connector 320×240 Pixel LCD screen | Amazon | About $15.00 |
*In addition to the items above, one tact switch, one LED, and one resistor of about 100Ω will also be needed.
Let’s use the ESP32 to display the date and time on the screen like a normal alarm clock so that the alarm can ring at a specified time. To make a “wake up alarm clock,” we need to place the extension button (for stopping the alarm) separately from the alarm clock body. The idea is that you would have to get up from bed and walk over to where the extension button is to turn off the alarm.
As shown in the video below, use two ESPs, one for the main unit and the other for the extension. In the video, the main unit and the handset are next to each other, but they can be separated if they’re connected by Wi-Fi.
Fig. 1 Transmission between the body and extension
For microcomputers such as Arduino, it’s common to be able to get the elapsed time upon booting up the program. However, the time you get is often not very accurate, as the elapsed time will reset when the power is turned off.
With ESP32, you can connect to the internet via Wi-Fi to get the date and time periodically from an NTP server on the internet and set it on ESP32. However, some microcomputers, such as Arduino Uno, don’t have an internet connection function, so it would be useful to have a mechanism that could handle date and time easier.
That’s why an IC called “RTC” (Realtime Clock) is commonly used. An RTC is an IC that makes time tick based on elements that are periodically signaled. In addition, by connecting it to an external power supply such as a battery, time can be maintained even while the power to the microcomputer is switched off.
Numerous products are in RTC, but this time, we’ll be using an RTC that’s been made into a module called “DS3231.”
The DS3231 module is popular among the RTC modules used in electronic work, and it’s readily available. Since the connection is I2C, only four wires are required. In addition to the RTC function, there’s also a temperature sensor function (however, the temperature sensor won’t be used in this article).
In addition, on the DS3231 of Photo 1, a button battery called “LIR2032” is installed so that the date and time will continue to be recorded even when the microcomputer is turned off. LIR2032 is the same battery size as CR2032, except it’s rechargeable.
Pic. 1 DS3231 Module
Since the clock is used to check the current date and time, it’s necessary to display the date and time in an easy-to-understand manner. The following devices are used to display the date and time.
There are different libraries and different programming methods depending upon the device. It also depends upon whether it’s suitable for what you want to make. For example, a 7-segment LED is suitable for displaying only numbers at a low cost, but won’t be suitable for a detailed display. Among the above, the graphic LCD display is the most versatile and can be used for many different projects, so we decided to use it for today’s demonstration.
There are various types of LCD display modules out there, but for today’s project, we’ll be using a controller mount called “ILI9341” and be using an SPI connection (Photo 2). In addition, LCD displays are commonly sold in 2.2-inch/ 2.4-inch /2.8-inch sizes, so adjust accordingly to the type of work you’re doing.
Pic. 2 ILI9341 2.8 inch LCD display for controller
Since it’s an alarm clock, having a sound ring out at a specified time is a necessity. You can connect a buzzer to the ESP32 to produce a single sound, but if you’d like, you could also use your own favorite sound. For that, we’ll be using a module called “DFPlayer Mini” that can play back any MP3 data (Photo 3).
DFPlayer Mini is a module that can play MP3 in a microSD card by sending commands via a serial connection. A small speaker can be connected to the speaker output pin to produce sound.
Pic. 3 DFPlayer Mini
Let’s get into the actual production. First, wire the alarm clock.
Use two breadboards, one with ESP32 and DS3231, and the other with LCD and DFPlayer Mini. The wiring of each part is as shown in Figure 2.
Since the ESP32 is wide, you can only put a jump wire on one side of a normal breadboard. Therefore, use a breadboard with only one side of the power line and one row of holes instead (such as SAD-101 from Sanhayato).
ESP32 and LCD displays are connected by SPI. ESP32 can use two SPIs (VSPI and HSPI), but for VSPI, (18/19/23 pin) is used (Table 1).
ESP32 and DS3231 are connected via I2C. In ESP32, I2C can be assigned to any pin, but standard pins (SDA = 21 and SCL = 22) are used (Table 2).
DFPlayer Mini connects serially. ESP32 can use three serials, but for this, use the combination of 16th pin and 17th pin (Table 3). Also, connect speakers to the DFPlayer Mini “SPK1” and “SPK2” pins.
Fig. 2 Wiring for alarm clock body
ESP32 pin | LCD display pin |
5V | VCC |
GND | GND |
5 | CS |
4 | RESET |
2 | DC |
23 | MOSI |
18 | SCK |
19 | MISO |
Table 1: Connection between ESP32 and LCD
ESP32 pin | DS3231 pin |
5V | VCC |
GND | GND |
21 | SDA |
22 | SCL |
Table 2: ESP32 and DS3231 connection
ESP32 pin | DFPlayer Mini pin |
5V | VCC |
GND | GND |
16 | TX |
17 | RX |
Table 3: ESP32 and DFPlayer Mini connection
Next, we’ll be doing the wiring for the extension. The wiring for this should look like Fig. 3. All you have to do is connect the switch and LED to the ESP32. Connect one side of the switch to the 3V3 pin of ESP32 and the other side to pin 4. Connect to GND via ESP32 pin 13 via resistor → LED.
On the circuit that reads the switch status, insert a pull-up resistor or pull-down resistor. However, since the ESP32 can perform pull-up/pull-down with an internal resistor, the external resistor is omitted.
Fig. 3 Extension circuit
When you finish working on the circuits, you can create the program. First, start with installing each of the below libraries.
The installation procedure is as follows.
There are several RTCLib and DFPlayer libraries with similar names. RTCLib installs “RTCLib by Adafruit” while DFPlayer installs “DFRobotDFPlayerMini by DFRobot.”
Fig. 4 Adafruit GFX library installation
Additionally, install a font file to display the time in large characters. If you download and unzip the following zip file, you’ll get a file called “FreeSans40pt7b.h.”
Open Arduino IDE’s standard sketch folder, open “libraries”-> “Adafruit_GFX_Library”-> “Fonts” folder, and copy the font file there.
https://www.h-fj.com/deviceplus/font.zip
Next, create an alarm clock program in Arduino IDE and write it to ESP32. The contents of the program are shown in Listing 1.
List 1: Alarm clock body program
Programming table(Put here)
However, the 17th to 21st lines need to be rewritten as follows.
・Line 17/18
Rewrite according to the SSID / password of your Wi-Fi router.
・Line 19
Specify the IP address to be assigned to ESS32. Decide the IP address yourself according to the network configuration of your Wi-Fi router.
In normal IP addresses, four numbers are separated by a period, but in this line, it’s in the form of a function argument, so the four numbers are separated by commas.
・Line 20
Specify the IP address of the default gateway for the network. Usually, it’s the IP address of the Wi-Fi router. Separate the four numbers in the IP address with commas.
・Line 21
Rewrite according to the IP address assigned to ESP32 of the extension unit.
For example, if you want to set it up as shown in Table 4, rewrite lines 17-21 as shown in Listing 2.
Item | Setting value |
Wi-Fi Routers SSID | my_wifi |
Wi-Fi Router password | my_password |
IP address assigned toESP32 body | 192.168.1.101 |
Default gateway IP address | 192.168.1.1 |
IP address assigned to ESP32 of the extension unit | 192.168.1.102 |
Table 4: Example of network settings of the main unit
List 2: Example of rewriting lines 17-21
On the other hand, the extension program looks like List 3.
Rewrite lines 5 to 9 in the same way as the alarm clock itself. However, on the 7th line, specify the IP address assigned to the extension unit. In addition, specify the IP address of the alarm clock in the “Main console IP address” on the 9th line.
List 3: Extension program
Programming table
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 |
#include #include #include #include <Fonts/FreeSans12pt7b.h> #include <Fonts/FreeSans18pt7b.h> #include <Fonts/FreeSans40pt7b.h> #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 = "<!DOCTYPE html>\n" "<html>\n" "<head>\n" "<meta charset=\"utf-8\">\n" "<title>Alarm clock settings</title>\n" "</head>\n" "<body>\n" "<form method=\"get\" action=\"/set\">\n" "<p>\n" "<select name=\"hour\">\n"; for (i = 0; i < 24; i++) { html += "<option value=\""; html += String(i); html += "\">"; html += String(i); html += "</option>\n"; } html += "</select>(h)\n"; html += "<select name=\"min\">\n"; for (i = 0; i < 60; i++) { html += "<option value=\""; html += String(i); html += "\">"; html += String(i); html += "</option>\n"; } html += "</select>(min) \n"; html += "<input type=\"submit\" name=\"submit\" value=\"Alarm setting\" />\n"; html += "</p>\n"; html += "</form>\n"; html += "<form method=\"get\" action=\"/set\">\n"; html += "<input type=\"hidden\" name=\"off\" value=\"1\">\n"; html += "<p><input type=\"submit\" name=\"submit\" value=\"Alarm off\" /></p>\n"; html += "</form>\n"; html += "</body>\n"; html += "</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 < server.args(); i++) { if (server.argName(i).compareTo("off") == 0) { is_off = true; break; } else if (server.argName(i).compareTo("hour") == 0) { s_hour = server.arg(i); } else if (server.argName(i).compareTo("min") == 0) { s_min = server.arg(i); } else if (server.argName(i).compareTo("sec") == 0) { s_sec = server.arg(i); } } // Turn off alarm if "off" parameter is set if (is_off) { strcpy(alarm_time, "Off"); server.send(200, "text/plain; charset=utf-8", "Alarm turned off."); } // Set alarm time else if (s_hour.length() > 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 <= 23 && min >= 0 && min <= 59 && sec >= 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) { tft.drawRect(30, 180, 260, 40, ILI9341_RED); } else if (alarm_ctr == ALARM_SIG) { tft.drawRect(30, 180, 260, 40, ILI9341_BLACK); } alarm_ctr++; alarm_ctr %= ALARM_SIG * 2; } // Update date/time using NTP at a specific time everyday if (strstr(new_time, adjust_time) != NULL) { if (!ntp_adjusted) { setTimeByNTP(); ntp_adjusted = true; } } else { ntp_adjusted = false; } delay(20); } |
After the program has been written to the main console/extension unit, check the operation. The procedure is as follows.
Pic. 4 Alarm settings
To cancel the alarm setting, connect to the address “http: // IP address of the machine/” with a web browser and click on the “Alarm Off” button.
You can connect to the ESP32 network via Wi-Fi. On top of being able to connect to the internet as with today’s example, you can now connect to a wide array of electronic work on the network.
Want to keep exploring the capabilities of Arduino? Check out some related articles: