The Alarming Clock

The Alarming Clock is a DIY alarm clock and MP3 player. I built it for my wife who wanted different alarm times for different days of the week. The clock has a TFT touch screen that displays the time and allows the user to play MP3 files from an SDCard and set and edit alarms. These functions can also be performed with a web browser on the local network.

The question is why build something whose function could be replaced with an app on a smart phone. The use of a dedicated clock over a phone is a matter of personal preference.

Summary

Hardware
ProcessorESP32 (DevKitC)
MP3 CodecVS1053b (Adafruit board)
Display/Touch ScreenILI9341 and XPT2046 with SDCard slot
Real-Time ClockBattery backed up DS3231
Audio Amp (optional)MAX98306 (Adafruit breakout) and speakers
Buzzer transducerProcessor generated tone
Custom PCBDeveloped in KiCad
Nonvolatile memoryPersistent alarm settings
Potentiometer and switchVolume control and alarm cutoff
Software
ESP32 version of FreeRTOS
Touch screen used to set alarms or play music
Web server supports setting alarms and playing music
Music or buzzer alarms
Power failure buzzer alarm
Once, weekly, and daily alarms (7 alarms)
Time set from the network using SNTP

Status (as of 1/2021)

HardwareLate beta. PCB has known errors with workarounds
SoftwareBeta. Testing is in progress.
3D Printed CaseBeta. Needs a way to secure speakers and buzzer


Old Case Without Speakers

Design

The Alarming Clock is built around the ESP32 dual core processor with Bluetooth and WiFi. See the history section for information about design choices.

When the clock is idle, it displays the time of day in either 12 or 24 hour format. From the time display, the user can select "Menu" for setting alarms and changing settings or "Play" to play songs. Each of the 7 alarms can be enabled or disabled and can either sound the buzzer or play a song at the alarm time. During power failures, songs are not played, and the buzzer is sounded regardless of the alarm setting. The user can select a single song to play at the alarm time or select a directory indicating all the songs in the directory are to be played. In other words, play lists are implemented as directories.

If the user selects "Play" from the time screen, the top-level directory on the SDCard is shown. The user selects a song to play by touching it. For directories, the user can touch the lefthand side to play all the songs in the directory or the center of the directory to open it for exploration. When a song is playing, player controls are displayed that allow the user to select the previous or next song or to pause or stop play.

When a song is played during an alarm, the audio volume begins low and ramps up to the current volume control setting to make the transition from sleeping to waking easier.

The diagram gives an overview of the system architecture. The VS1053b MP3 player, the TFT display, and the display touch controller all share an SPI bus, which is called HSPI for the ESP32. The SDCard requires a dedicated SPI bus and uses the VSPI bus. The DS3231 real-time clock (RTC) communicates with the ESP32 using an I2C bus.

The MP3 breakout board drives an optional MAX98306 class D audio amplifier that drives 40mm speakers. Using a stereo connector, the output of the MP3 board can be connected to an external amplifier or headphones.

The system is powered from a 5 volt source; however, the ESP32 runs from a 3 volt battery during a power mains failure. The goal is to power the ESP32 long enough so that it can sound a morning alarm if the power fails during night time. The RTC also has a battery backup for longer power failures.

The ESP32, VS1053b, and MAX98306 are contained on breakout boards which plug into a custom printed circuit board. A DS3231 is soldered to the custom PCB.

Software for the system consists of a number of tasks.

FreeRTOS queues and shared variables are used for intertask communications. The main task initiates the playing of an MP3 file by setting the file to play in a shared variable and then sending a queue message to the player task. The player task opens the file and sends 32-byte blocks of it to the VS1053b. When the player task is finished playing a song, it sends a queue message back to the main task.

There is an interrupt handler in the watcher task that monitor a GPIO pin connected to a voltage divider. This pin is normally at logic one but falls to logic zero during a power failure. The interrupt handler detects the power failure and queues a message to the watcher task. The watcher task wakes up and puts the ESP32 into light sleep mode to conserve battery power. The processor remains asleep until either power is restored or the RTC signals that the first alarm has triggered. In the latter case, the processor produces a buzzer sound until the user presses the alarm switch or the battery is exhausted.

The volume control is connected to an ADC (analog-to-digital converter) on the ESP32. This is a linear potentiometer. To give a wider range of volumes, the pot value is modified with a natural log function in software. The processor also monitors a switch conntected to a GPIO pin which is used to end a buzzer alarm.

History

When I started the project, I decided to use the Arduino Pro Mini. I was able to build an MP3 player with the Pro Mini and the Adafruit VS1053b board; however, I ran out of flash and RAM. I was looking at a Teensy with more resources when I became interested in the ESP32 which had plenty of RAM and flash for the project.

The ESP32 is supported by two toolchains: Arduino and a version of FreeRTOS. The former is implemented on top of the latter, and FreeRTOS calls can be made from an Arduino application. I chose to use the FreeRTOS (ESP-IDF or IoT Development Framework).

The ESP32 IDF includes an SPI driver. The driver seemed incomplete when I first started the project, and the documentation wasn't clear to me. I had to empirically determine the delay between sending a command to a device and when the data was available.

I originally used the ESP32 TFT library by github user loboris. Due to SPI driver problems, this library contained a modified SPI library. I originally used this SPI library, but was forced to abandon it when I upgraded the IDF to get SDCard support. I tried to use the system SPI driver and the SPI library from the TFT package, but they would allow concurrent operation. I eventually dropped the TFT library and wrote my own TFT support code.

Because the IDF requires a dedicated SPI bus for the SDCard, I could not use the SDCard slot on the VS1053b breakout board since the MP3 player and the SDCard slot were connected to the same SPI pins. So I used the SDCard slot on the TFT card. SDCards in the TFT SDCard slot would only support very slow read rates which were too slow to play music. The problem is that the TFT card used "poor man's level shifters", i.e., resistors, to allow a 5 volt device to talk to the SDCard. The resistors introduced an RC delay, thus reducing the maximum transfer speed. I replaced the resistors with jumpers.

I had a custom PCB fabricated. When I plugged it into 5 volts, nothing happened. It turned out the power connector was reversed. Fortunately, the Low Dropout Regulators (LDO) on the ESP32 and VS1053b handled the power reversal without an damage. Since the TFT board is powered from the VS1053b LDO, it was not damaged. I had not installed the RTC.

After I reversed power to the PCB, the system worked; however, MP3 files would only play at reduced speed. I thought the problem was related to the power reversal, but it was not. I had made a software change right before I had the PCB board fabricated that I had not tested. Before playing a song, I reset the VS1053b. This software reset resets the clock divider on the MP3 board which results in MP3's being played slowly.

Since the ESP32 has support for I2S, I considered using that feature instead of an MP3 player board. I did some experiments, but I abandoned using I2S because of the large size of the library to decode MP3.

I originally planned to use an NE555 oscillator to drive a transducer for the alarm buzzer. The buzzer sounds under two conditions:

With the 555 buzzer, I designed a logic circuit that would sound the alarm under these two conditions. This design added a quad NOR gate package, a D-flip-flop, and the NE555, so I abandoned it for a simpler solution where the ESP32 creates the alarm sound using a DAC. During a power failure, the ESP32 enters sleep mode. If the DS3231 triggers an alarm before power is restored, the ESP32 wakes up and sounds the alarm. During power failures, the ESP32 runs off a battery.

There was a problem with the display touch controller. With one ESP32, the touch controller would fail to initialize about once in fifty processor reboots. To allow the touch controller to signal a touch by pulling a line (INT) low, the controller has to be put into low power mode by sending a command to read a register with the low bit cleared. With a second ESP32, the touch controller would never bring the INT line low. I used a logic analyer to determine the problem. During an SPI transfer, the receiver and transmitter are both active. After a command is sent to the touch controller, data is send back to the processor. During this time, the touch controller receives whatever data is on the processor transmission bus. I had assumed that the SPI API call I had made would send zeros during the time data was being received from the touch controller. This was not the case and random data was sent to the controller chip. If the data ended in a zero bit, the touch controller was put into low power mode and worked as expected. Otherwise, the bit indicated that low power mode was not to be entered. I added data to the transmission that was padding zero bytes.

I noticed that the speakers became hot when the unit was powered on even when there was no audio playing. The problem was a defective Adafruit audio amp. Replacing the amp fixed the problem.


benjy at tuxcat.com