The “ESP Alarm” is a connected
alarm with your smart phone via Wi-Fi using ESP8266 module. You can
add/modify/delete/activate/deactivate alarms using an Android
application when the device is up and connected with the same Wi-Fi
network which your phone is connected to.
So I decided to make an “ESP Alarm” device that allows me to set alarms using my smartphone through Wi-Fi and leave the rest to the alarm clock. In simple terms, it’s a Wi-Fi-enabled, IoT alarm clock!
This is my second project using the tiny monster ESP8266 Wi-Fi module. Check out my first project, "How to Build a Control Circuit with Adjustable Working Time via Wi-Fi" here on AAC.
Important Notes
- This application requires Android 4.4 (Marshmallow) and up.
- The maximum number of alarms is 20 due to hardware limitations.
- This app is under development and still missing some enhancements, but it meets the main requirements for this project.
Overview of the Device
Part | Documentation | QTY |
---|---|---|
Arduino UNO or any compatible board (optional) | https://www.arduino.cc/en/Main/ArduinoBoardUno | 1 |
ATmega328P (optional) | www.atmel.com/devices/ATMEGA328P.aspx | 1 |
ESP8266 Wi-Fi module, ESP-01 model | www.esp8266.com/wiki/doku.php?id=esp8266-module-family | 1 |
1.44-inch TFT display module | https://world.taobao.com/item/521610992161.htm | 1 |
TP4056 breakout board (Li-ion battery charger) | https://world.tmall.com/item/38825413372.htm | 1 |
DS1307/ DIL-08 package (RTC chip) | https://www.maximintegrated.com/en/products/digital/real-time-clocks/DS1307.html | 1 |
74LVX4245/ TSSOP-28 package (5V-3V3 level converter) | https://www.fairchildsemi.com/products/logic/voltage-level-translators/voltage-level-translators/74LVX4245.html | 1 |
USB TTL converter cable (optional) | http://www.ftdichip.com/Products/Cables/USBTTLSerial.htm | 1 |
The “ESP Alarm” is connected with an Android application via Wi-Fi using the ESP8266 module. You can add/modify/delete/activate/de-activate alarms using the Android app when the device is up and connected with the same Wi-Fi network your phone is connected to.
My “ESP Alarm” has a 1.4-inch TFT screen and two buttons as a user interface. One button is used to turn off the alarm and the other is to snooze it. The welcome message (for when the user turns the alarm on) and the snooze time are settable through the app. Time and date are shown on the TFT screen and synchronized with your phone's time/date.
Changing or adding new alarms can be done with a handshake process. This means that, if the app didn’t receive a response from the device, then it will not do the desired action. We decided to add this restriction to avoid any mis-synchronization problems between your smartphone and the “ESP Alarm”.
Alarms can be set to be one-time-only or repeatable (based on the day). Each one has a unique title shown on the TFT screen when it goes off (so you can remind yourself of specific appointments, recurring meetings, etc.).
Hardware
The ESP8266 is connected with an Arduino UNO via UART connection using AT command (commands in ASCII). You can add/modify/delete/activate/deactivate alarms using the Android app when the device is up and connected with the same Wi-Fi network your phone is connected to.I used a 1.4-inch TFT screen to print out some important information like time, date, IP address, port number, title of the alarm, etc. Both the TFT screen and Wi-Fi module use 3V3 logic level and need a level converter with Arduino which uses 5V logic level.
Time and date are kept in the RTC chip, DS1307, which is connected with Arduino using an I2C interface. I added a Li-ion backup battery to maintain a correct date and time even if the device is turned off. To charge this battery, I used a charger IC called TP4056.
TFT Screen
I used a 1.44-inch, 128x128 resolution, SPI TFT with an ILI9163C internal driver. These modules come in two versions, one with a 3V3-5V on-module level converter IC and another one without this IC.1.44" SPI TFT module versions
I bought one of the 3V3 modules from a Chinese supplier but, for some reason, I received the 5V version instead of the 3V3 version.
Anyway, I added an external level converter IC which is 74LVX4245. It’s an 8-bit translating transceiver that is designed as an interface between a 5V bus and a 3V bus in a mixed 3V/5V supply environment. This IC has 8-pin A port for 5V logic, an 8-pin B port for 3V logic, and a Transmit/Receive (T/R#) input pin to determine the direction of data flow (from A to B or B to A). In my case, I fixed the direction from A (5V) to B (3V3).
To interface with this TFT module, you need to add the TFT_ILI9163C library (written by Sumotoy) to your Arduino IDE and to connect the TFT pins with Arduino as follows:
Pin From TFT | Pin From Arduino | Note |
---|---|---|
VCC | +3V | |
LED | +5V through a resistor | The module has a current-limiting resistor but I found it’s safer to add another one with low value such as 100 - 1K ohm. |
GND | GND | |
SCL (SPI Clock) | PIN 13 | SCL is not related to the “SCL” pin in I2C protocol. The datasheet of ILI9163C used the same abbreviation. |
SDA (SPI MOSI) | PIN 11 | SDA is not related to the “SDA” pin in I2C protocol. The datasheet of ILI9163C used the same abbreviation. |
D/CX | PIN 9 (optional) | Display data / Command selection pin |
CS | PIN 10 | Chip Select |
RES | +3V | Reset |
Wi-Fi Module
The ESP8266 is a low-cost SoC chip with an embedded microcontroller and a full TCP/IP protocol stack, which means that it can directly access your Wi-Fi network. Because this chip has its own MCU, you can put your application code within it or you can use the module just as a Wi-Fi transceiver—like what we are going to do in this project. This is done using AT command (again, commands in ASCII) using a UART connection. You can view the full list of AT commands in this document.Image taken from the ESP8266 Wi-Fi Module Quick Start Guide
The ESP8266 chip comes in different module models but we'll use the ESP-01 model.
The TX signal from the ESP8266 can be left without any conversion to 5V as long as the minimum input-high voltage at VCC = 5V is about 2.65 V according to Figure 35-25 from the ATmega328P's datasheet (the Arduino UNO's controller).
Image taken from the ATmega328P datasheet
However, in the same datasheet, (Table 30-1) the lowest value where the pin is guaranteed to be read as high is 0.6×VCC= 0.6×5 =3V. Also, according to (Table 5-1) in the ESP8266EX datasheet, the minimum output-high voltage is 0.8×Vio = 0.8×3.3 = 2.64V which is a little bit smaller than the minimum input-high voltage of ATmega328.
So the connection could be unreliable in the worst case. I decided that it’s safer to convert from the 3V level to 5V using a simple circuit comprised of one MOSFET and two pull-up resistors:
5V-3.3V bidirectional level converter
RTC Chip
DS1307 is the RTC chip used to get time from its internal non-volatile registers while there is a power supply or a backup battery. This IC uses an I2C interface with its MCU which consists of two lines: SCL (Serial Clock) and SDA (Serial Data). I used a library called DS1307RTC to deal with it on Arduino, which you can get from the PJRC website.I used a Li-ion battery (from my old phone, the NOKIA C5) with a TP4056, a complete constant-current/constant-voltage linear charger for single cell lithium-ion batteries. Just to make things easier, I used a breakout board like the one in the photo.
The TP4056 breakout board. Image source: HAOYU Electronics
You can change the charging current by changing the resistor Rprog value. For more information, please refer to the charger’s datasheet.
Arduino Code
The code depends on the following libraries:- TFT_ILI9163
- Adafruit_GFX
- DS1307RTC
- Wire (for I2C connection)
- EEPROM (to store alarms in the internal EEPROM)
- SoftwareSerial (optional debugging)
The Wi-Fi module must be configured with your network SSID/password and other settings, like your port. You must change this part:
sendCommand("AT+CWJAP=\"YOURSSID\",\"YOURPASSWORD\"\r\n", 1000, DEBUG);
Note: Make sure to wait about six seconds (delay(6000);) after that because the module needs some time to connect to the network.
If everything works, your module will obtain an IP using this command:
IP = sendCommand("AT+CIFSR\r\n", 1000, DEBUG);
Checking the Obtained IP
I added a simple method to check the obtained IP.Usually, for a local connection with your router, the IP will be something like 192.168.1.100. The response from the module for this command “AT+CIFSR” will be something like:
+CIFSR:STAIP,"192.168.1.50"
+CIFSR:STAMAC,"18:fe:34:9f:48:d8"
And if the connection is failed, the response will be:
ERROR
So I found that the simplest way to find an error is to check the length of the response.
Note: I get the IP from the response using the substring function of the String object IP between characters 25 and 38. The response starts with AT+CIFSR\r\n+CIFSR:STAIP,” which is 25 characters
IP = sendCommand("AT+CIFSR\r\n", 1000, DEBUG); // get ip address
if (IP.length() <= 25)
{
IP = "NOT Avaliable";
tft.print(" Not \r\n Connected :(");
return 0;
}
else
{
IP = IP.substring(25, 38); // Get the IP part from the command response
tft.print("Connected ");
return 1;
}
Communication Commands
The communication commands received from the mobile application and the expected corresponding responses from the Arduino side are shown in this table:Command | Response | Note |
---|---|---|
SET | CT | Setting the snooze time and welcome message. Example: SET,5,Hello; |
ADD | AT |
Adding an alarm. ADD,ID,A(active)/D(deactive),HHMM,RepDays,SoundType,Title; |
DEL | DT | Deactivating/deleting an alarm |
SYN | ST | Syncing the time between the RTC chip and your mobile. SYN,DD+MM+YYYY,HH:MM:SS, |
DEB | No response | Printing some internal information on the screen. |
RES | RT | Resetting the alarm, including flashing the internal EEPROM. |
To keep alarms saved even when you turn off the device, I used the internal EEPROM which is not the best choice because it has a limited number of times to read/write its contents. The EEPROM has an endurance of at least 100,000 write/erase cycles. I think it’s better to use external memory in a future development.
The memory structure is simple. 22 bytes for every alarm, as follows:
- B0:ID-B1:A(Active)/D(Deactive) as char
- B2:HH(Hour) as number
- B3:MM(Minuit) as number
- B4:Rep Day as number interpreted as flags
- B5:reversed
- B6..B21: Title 16 character
- 20 Alarms so 20×22 equals 440B, Address:000..439
- 16B for welcome message, Address:444..459
- 1B snooze time, Address:460
- O: No repetition
- F: Repeat all days.
- A: A Sequence of numbers. (e.g., 123 means to repeat this alarm on Sunday, Monday, and Tuesday. I use this sequence to format a HEX number. For example, the 1000001 value means that the alarm is repeated every Sunday and Saturday.)
I’ve decided to use a simple method in processing and for saving these alarms in the RAM. Simply, whenever the alarm’s hour, minute and day matches the current date/time, then the alarm should go off. To do that, three arrays are defined. They are:
- Active_Alarms_H (size = 24 for 24 hours in a day)
- Active_Alarms_M (size = 60 for 60 minutes in an hour)
- Active_Alarms_D; (size = 7 for 7 days in a week)
Active_Alarms_H[1]=00000000000000000010
Active_Alarms_M[15]= 00000000000000000010
Active_Alarms_D[0]= 00000000000000000010
Simply put, to know which alarm should go every minutem I do an “AND” operation between the current time elements from these arrays, as follows:
activeAlarms = Active_Alarms_H[tm.Hour] & Active_Alarms_M[tm.Minute] & Active_Alarms_D[DayOfWeek - 1];
For example, let's say we have three alarms, as follows:
- Alarm0: 12:15, Rep:Sun
- Alarm1: 14:00, Rep:Sun,Sat
- Alarm2: 14:15, Rep:Fri
Active_Alarms_H[12] = 00000000000000000001
Active_Alarms_M[15] = 00000000000000000001
Active_Alarms_D[0] = 00000000000000000001
--------------------------
Active_Alarms_H[14] = 00000000000000000010
Active_Alarms_M[0] = 00000000000000000010
Active_Alarms_D[0] = 00000000000000000011
Active_Alarms_D[1] = 00000000000000000010
--------------------------
Active_Alarms_H[14] = 00000000000000000110
Active_Alarms_M[15] = 00000000000000000101
Active_Alarms_D[6] = 00000000000000000100
Suppose that it’s now 14:15 on Friday, so:
activeAlarms = Active_Alarms_H[14] & Active_Alarms_M[15] & Active_Alarms_D[6];
00000000000000000110
00000000000000000101
00000000000000000100
-------------AND-------------
00000000000000000100
Consequently, the alarm with ID=2 must go off now.
***A note about memory:
The last thing I would like to mention is that I suffered from low RAM free space, which made the program behave in ways I didn't expect. I tried to do some optimization in the code to fix this. For example, I’ve decided to use the PROGMEM option, which makes the compiler store the desired variable in flash memory. I’ve saved 52 bytes by storing the header of HTTP in the flash rather than in RAM.
In the below table, you can find three important defines in the code and how to use them:
Define | Description |
---|---|
#define SWDebug |
To enable software serial debugging using (PIN 2,3). Don’t enable
it if you’re using Arduino UNO. There is not enough free space in the
RAM memory. The SoftwareSerial library has been developed to allow serial communication on other digital pins of the Arduino, using software to replicate the functionality of the hardware UART. |
#define HTTPResponse | To enable HTTP packeting |
#define DebugOnTFT | To enable debugging on TFT |
Notes:
- sendHTTPResponse, sendData, and sendCommand are mostly based on an AllAboutEE tutorial.
- If you use ESP8266 modules with an old SDK, you may suffer from bugs like I did. The only solution, in this case, is to update your firmware to the latest version. Check this AAC article out for assistance with updating the firmware. As of the publication of this article, I’ve upgraded my firmware from version 1.3 to 1.5.4.
PCB & Schematic
Here are some notes to help you understand the PCB and schematic design:ESPAlarm PCB (top view)
ESPAlarm PCB (bottom view)
- I embedded an Arduino in my device, so I added an ATmega328P. I also made an option for you to connect an Arduino UNO, but you’ll need to choose between these two options—don’t connect them both.
- I added two jumpers for the Wi-Fi module: one to switch it off/on and the other to select the boot mode (normal start(GPIO0 pulled up) or upgrade firmware(GPIO0 pulled down)).
- You can find a header called “WIFI_MOD_PROG”. This header is to connect the external serial-USB cable (like the one in the image below) with a Wi-Fi module.
A USB TTL serial cable. Image courtesy of FTDI Chip.
- JP1 is used to select the type of cable, 3V3 or 5V. According to your cable type TX, the signal will be converted to 3V3 or connected directly with the ESP8266.
- I added an RGB LED to show the device's power status. I also added an expansion header for future development.
- PCB-wise, it’s the first time I've tried “negasilk.ulp” to make an inverted silkscreen like the one in the image. To learn how to use this ULP, please refer to this short tutorial.
-
The device takes the power from the Arduino or from the charger breakout USB jack.
Software
Click to enlarge
App Main Functions
We can divide our main functions into three areas.First, we have the action bar where we have four buttons:
- Add: To “Add” new alarms. We'll go over this later.
- Sync: To perform a date/time synchronization with the ESP Alarm.
- Settings: To open the “Settings". We'll go over this later, too.
- About: Displays a pop-up with info.
Finally, the third function is the list of your pre-set alarms. This will, of course, be empty when the user runs the app for the first time. In this case, tapping inside this area will open the “Add Activity”. However, after adding alarms, this area will show the alarms' time and title with a delete button for each alarm. If the user taps on any record, the app will launch the “Edit Activity”.
"Add" Function
Simply fill in your alarm’s title with the specific time. And don't forget to activate the alarm!You can choose the days you would like this alarm to be repeated by checking these days from the repeat days list.
Note that “Edit Activity” will allow the same changes. However, it will be automatically filled up with the selected alarm’s data.
"Settings" Function
- The snooze period is limited to four options. The user must pick one of them.
- The welcome message is optional for the user—but it is really awesome. For example, it allows the user to name his ESP Alarm, especially if he has more than one.
- The user has to fill the IP address that is displayed on the ESP Alarm’s screen after it gets connected. The same goes for the port number.
- "Reset all alarms" is a very critical button. Once the user clicks it, all alarms will be removed from the ESP Alarm and, if it gives back a reset response, then all alarms will be removed from the phone, too.
- Go to settings and fill in the IP address and port fields.
- Press save. If you get a success message, then you're on the right path.
- Press the sync button in the main activity to make sure your RTC has the right timing. This completes the initializing stage.
- You should now be able to start adding your alarms as you wish!
No comments:
Post a Comment