ESP Alarm: Make an IoT, Wi-Fi Enabled Alarm Clock with an ESP8266

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.

It’s common to set-up alarms using our mobile phones to wake up in the morning—and it’s not unusual to set-up several alarms in an attempt to wake up at a specific time. The problem is that, after we finally wake up, sometimes our phone’s battery is drained during the battle to get up from bed!

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

  1. This application requires Android 4.4 (Marshmallow) and up.
  2. The maximum number of alarms is 20 due to hardware limitations.
  3. This app is under development and still missing some enhancements, but it meets the main requirements for this project.

Many thanks to my friend Jihad Al-bathish (AKA Joud) for developing a native Android application for this project.

 

Overview of the Device

ESP Alarm Schematic

 

ESPAlarm Schematic

Click to enlarge

 

BOM

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, 128×128 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

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

 

 

Another important thing about this TFT is to know how to format a color. This TFT controller supports 18-bit, 16-bit, and 6-bit RGB interfaces. Sumotoy’s library uses 16-bit RGB format.

 

RGB565

 

This is known as RGB565 (5 bit red, 6 bit green, and 5 bit blue). There are some online tools that can give you your color of choice in this format.

 

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.

 

ESP8266 ESP-01 Model Pinout

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 module uses the 3V3 level, so we need to do a conversion between it and the Arduino, which uses the 5V level. Since we already used four pins of the 8-pin port from 74LVX4245 converter IC for the TFT screen, we’re going to use a fifth pin to convert the TX signal from the Arduino to 3V3 level.

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

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.

 

TP4056 Breakout Board

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)

First, the code initializes an SPI connection with the TFT screen, an I2C connection with the RTC chip, and a UART connection with the Wi-Fi module. Then it prints an intro screen which includes the AAC logo (I tried to match it with the original one as much as possible!).

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, Example: SYN,08+20+2016,09:28:56,1
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

The addresses are as follows:

  • 20 Alarms so 20×22 equals 440B, Address:000..439
  • 16B for welcome message, Address:444..459
  • 1B snooze time, Address:460

I receive all commands in ASCII, and then I process them and convert what needs to be converted, like the repeat days of each alarm.  I receive the following from mobile:

  • 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.)

In order to make the program faster and to reduce the number of accesses to the  EEPROM, once the device starts, the code searches for active alarms (by reading B1 for every stored alarm) and updates some variable values in the RAM.

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)

Every bit in every element in these arrays represents the counter alarm’s ID. That means alarm 0 can use bit 0 from every element. So let us pretend that we have an alarm with ID 1 that goes every Sunday at 1:15. This will reflect on the contents of the arrays as follows:

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

Then:

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 (top view)

 

ESPAlarm PCB (Bottom 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.

 

Negative Silkscreen

 

  • The device takes the power from the Arduino or from the charger breakout USB jack.

 

 

 

Software

 

Click to enlarge

 

The Android application lets you control all the functionalities needed to add/edit/delete your specific alarms. It also gives you the ability to synchronize both date and time between the ESP Alarm and your mobile phone. The app has three major functions, explained below.

 

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.

Second is the digital clock which displays the current time according to your phone’s settings 12/24 format.

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.

The best way to start using this app is:

  1. Go to settings and fill in the IP address and port fields.
  2. Press save. If you get a success message, then you’re on the right path.
  3. Press the sync button in the main activity to make sure your RTC has the right timing. This completes the initializing stage.
  4. You should now be able to start adding your alarms as you wish!