Hardware components | ||||||
![]() |
| × | 1 | |||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 |
This is a variation of a previous Chiming Clock project.
By using an ESP32 I have used the bluetooth BLE to connect to an android app to set the RTC DS3231 to the system time of the phone.
When the system is booted, the app should be connected within the first 20 seconds to stop the countdown. When the time is set and the app is disconnected
the clock will be displayed on the ST7735S colour digital display.
I have used the DF Player mini to play the chimes on the hour. The output is connected directly to a 2w speaker mounted into a cardboard box for bass and amplification. (I have used a buck to supply the regulated 5v)
I have included a 2200mf electrolytic cap to allow the esp32 to boot as the surge to the DFPlayer on power up and playing would cause the ESP32 to malfunction.
/* **************************************************************************
Digital Chiming Clock Project using ST7735 80 x 160 (RGB) .96 inch IPS Display
With an ESP32 Wemos D1 and a DS3231 RTC:
Set The Time Using Bluetooth Low Energy every bootup (20second scan)
Before Going To Clock Mode.
Pin Used With The ESP32 :
IO22 SCL RTC
IO21 SDA RTC
RST RST ST7735
GPIO 5 CS
GPIO 26 DC
GPIO 18 SCL/SCK *These markings are confusing
GPIO 23 MOSI
**************************************************************************
*/
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLE2902.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <DFRobotDFPlayerMini.h>
#include <SPI.h>
#include <DS3231.h> //https://github.com/NorthernWidget/DS3231/releases
#include <Wire.h>
#define TFT_RST -1
#define TFT_CS 5
#define TFT_DC 26
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
DFRobotDFPlayerMini myDFPlayer;
DS3231 myRTC;
// inverted color definitions
#define BLACK 0xFFFF
#define WHITE 0x0000
#define BLUE 0x07FF
#define RED 0xFFE0
#define GREEN 0xF81F
#define YELLOW 0xF800
#define BROWN 0x9F6D
#define L_BLUE 0x51E4
#define SERVICE_UUID "fe45c941-9b87-4ef1-ac31-990dd686f813"
#define CHARACTERISTIC_UUID "ebc2e890-8ad5-4d51-a4a1-0e60efc9df4a"
String BT_IN;
char inString[13];
byte L_Hour = 18; // Last Hour
byte L_Min = 43; // Last Minute
byte L_Sec = 10; // Last Second
byte L_Dow = 6; // Last Day Of The Week
byte L_Date = 11; // Last Date
byte L_Month = 11; // Last Month
byte L_Temp = 28; // Last Temperature
byte L_Year; // Last Year
byte YEAR = 20; // Year 2000
byte YEAR_2; // Last 2 digits of the year
byte Sec, Min, HOUR, Hour2, Dow, Date, MONTH, Temp; // Variables from RTC
bool h12Flag, pmFlag; //Unused 12h pm Flags
bool century = false;
bool Blink = 1;
byte Led = 2; // Led Pin Used On The ESP32 Wemos
String DAYS_OF_WEEK [] {"Dummy", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
BLEServer* pServer = NULL;
BLECharacteristic* pCharacteristic = NULL;
uint32_t value = 0;
bool deviceConnected = false;
bool deviceDisconnected = false;
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
deviceDisconnected = false;
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
deviceDisconnected = true;
}
};
void Set_DS3231 () {
while (deviceConnected) {
std::string value = pCharacteristic->getValue();
if (value.length() == 15) {
BT_IN = (value.c_str());
pCharacteristic->setValue("");
inString[0] = BT_IN[0]; //Y
inString[1] = BT_IN[1]; //Y
inString[2] = BT_IN[2]; //M
inString[3] = BT_IN[3]; //M
inString[4] = BT_IN[4]; //D
inString[5] = BT_IN[5]; //D
inString[6] = BT_IN[6]; //w
inString[7] = BT_IN[7]; //H
inString[8] = BT_IN[8]; //H
inString[9] = BT_IN[9]; //M
inString[10] = BT_IN[10]; //M
inString[11] = BT_IN[11]; //S
inString[12] = BT_IN[12]; //S
YEAR_2 = (((inString[0] - 48) * 10) + (inString[1] - 48));
// now month
MONTH = (((inString[2] - 48) * 10) + (inString[3] - 48));
// now date
Date = (((inString[4] - 48) * 10) + (inString[5] - 48));
// now Day of Week
Dow = (inString[6] - 48);
// now hour
HOUR = (((inString[7] - 48) * 10) + (inString[8] - 48));
// now minute
Min = (((inString[9] - 48) * 10) + (inString[10] - 48));
// now second
Sec = (((inString[11] - 48) * 10) + (inString[12] - 48));
myRTC.setClockMode(false);// set to 24h
myRTC.setYear(YEAR_2);
myRTC.setMonth(MONTH);
myRTC.setDate(Date);
myRTC.setDoW(Dow);
myRTC.setHour(HOUR);
myRTC.setMinute(Min);
myRTC.setSecond(Sec);
myRTC.turnOffAlarm(1);
myRTC.turnOffAlarm(2);
Serial.println("The Time Has Been Set");
delay(600);
BLEDevice:: deinit(true);
Serial.println("Bluetooth has been turned off");
delay(600); // Allow RTC registers to set before any read occurs
}
}
}
void setup () {
Serial1.begin(9600,SERIAL_8N1,10,9);
myDFPlayer.begin(Serial1);
pinMode(Led, OUTPUT);
tft.initR (INITR_BLACKTAB);
tft.setRotation (3); // Display Lasdscape
tft.fillScreen (BLACK);
Wire.begin();
BLEDevice::init("Chiming Clock");
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_INDICATE
);
pCharacteristic->addDescriptor(new BLE2902());
pCharacteristic->setValue("");
pService->start();
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(false);
pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
BLEDevice::startAdvertising();
BLEDevice::startAdvertising();
myDFPlayer.volume(15);
myDFPlayer.play(25);
delay (5000);
// Loop While Waiting For Bluetooth
for (int x = 26; x < 46; x++) {
if (!deviceConnected) {
myDFPlayer.volume(15);
myDFPlayer.play(x);
}
else {
x = 47;
}
digitalWrite(Led, Blink);
Blink = !Blink;
delay(800);
}
digitalWrite(Led, LOW);
Set_DS3231();
BLEDevice::deinit(); // Turn Off Bluetooth
LinesDraw(); // Draw Lines Routine
MONTH = myRTC.getMonth(century);
if (century) { // Year 2100
YEAR = 21;
}
YEAR_2 = myRTC.getYear();
L_Year = YEAR_2;
TextDraw(); // Display All Screen Text
tft.setTextSize(3); // Time Text Size
}
void loop () {
Sec = myRTC.getSecond();
if (Sec != L_Sec) {
Sec_Print();
}
Min = myRTC.getMinute();
if (Min != L_Min) {
Min_Print();
}
HOUR = myRTC.getHour(h12Flag, pmFlag);
if (HOUR != L_Hour) {
Hour_Print();
}
Dow = myRTC.getDoW();
if (L_Dow != Dow) {
tft.setTextSize(2);
Dow_Print();
tft.setTextSize(3);
}
Date = myRTC.getDate();
if (L_Date != Date) {
tft.setTextSize(2);
Date_Print();
MONTH = myRTC.getMonth(century);
if (L_Month != MONTH) {
Month_Print();
YEAR_2 = myRTC.getYear();
if (YEAR_2 != L_Year) {
if (century) {
YEAR = 21;
}
Year_Print();
}
}
tft.setTextSize(3);
}
Temp = myRTC.getTemperature();
if (L_Temp != Temp) {
tft.setTextSize(2);
Temp_Print();
tft.setTextSize(3);
}
if ((Min == 59) && (Sec == 40)) {
Hour2 = HOUR + 1;
chime();
}
}
void LinesDraw() { //Draw The Screen Borders
tft.drawLine(3, 54, 157, 54, RED);
tft.drawLine(3, 55, 157, 55, RED);
tft.drawLine(3, 80, 157, 80, RED);
tft.drawLine(3, 81, 157, 81, RED);
tft.drawLine(112, 56, 112, 79, RED);
tft.drawLine(113, 56, 113, 79, RED);
tft.drawRect(1, 26, 159, 80, RED);
tft.drawRect(2, 27, 157, 78, RED);
tft.fillRect(114, 55, 44, 25, BROWN); // Temperature Box
}
void TextDraw() { //Set up Startup Screen
tft.setTextSize(3);
tft.setTextColor(WHITE);
tft.setCursor(10, 30);
tft.print(L_Hour);
tft.print(":");
tft.print(L_Min);
tft.print(":");
tft.print(L_Sec);
tft.setTextSize(2);
tft.setTextColor(GREEN);
tft.setCursor(4 + ((108 - (DAYS_OF_WEEK [L_Dow].length()) * 12) / 2), 60);
tft.print(DAYS_OF_WEEK [L_Dow]);
tft.setTextColor(L_BLUE);
tft.setCursor(20, 86);
tft.print("11/11/");
tft.print(YEAR);
tft.print(L_Year);
tft.setTextColor(YELLOW);
tft.setCursor(120, 60);
tft.print("28c");
}
void Sec_Print () { // Print Seconds
tft.setTextColor(BLACK);
tft.setCursor(118, 30);
if (L_Sec < 10) {
tft.print("0");
}
tft.print(L_Sec);
L_Sec = Sec;
tft.setTextColor(WHITE);
tft.setCursor(118, 30);
if (L_Sec < 10) {
tft.print("0");
}
tft.print(L_Sec);
}
void Min_Print () { // Print Minutes
tft.setTextColor(BLACK);
tft.setCursor(64, 30);
if (L_Min < 10) {
tft.print("0");
}
tft.print(L_Min);
L_Min = Min;
tft.setTextColor(WHITE);
tft.setCursor(64, 30);
if (L_Min < 10) {
tft.print("0");
}
tft.print(L_Min);
}
void Hour_Print () { // Print Hours
tft.setTextColor(BLACK);
tft.setCursor(10, 30);
if (L_Hour < 10) {
tft.print("0");
}
tft.print(L_Hour);
L_Hour = HOUR;
tft.setTextColor(WHITE);
tft.setCursor(10, 30);
if (L_Hour < 10) {
tft.print("0");
}
tft.print(L_Hour);
}
void Dow_Print() { // Print Days Of The Week String
tft.setTextColor(BLACK);
tft.setCursor(4 + ((108 - (DAYS_OF_WEEK [L_Dow].length()) * 12) / 2), 60);
tft.print(DAYS_OF_WEEK [L_Dow]);
L_Dow = Dow;
tft.setTextColor(GREEN);
tft.setCursor(4 + ((108 - (DAYS_OF_WEEK [L_Dow].length()) * 12) / 2), 60);
tft.print(DAYS_OF_WEEK[L_Dow]);
}
void Date_Print() { // Print Date
tft.setTextColor(BLACK);
tft.setCursor(20, 86);
if (L_Date < 10) {
tft.print("0");
}
tft.print(L_Date);
L_Date = Date;
tft.setTextColor(L_BLUE);
tft.setCursor(20, 86);
if (L_Date < 10) {
tft.print("0");
}
tft.print(L_Date);
}
void Month_Print() { // Print Month
// 56x 86y
tft.setTextColor(BLACK);
tft.setCursor(56, 86);
if (L_Month < 10) {
tft.print("0");
}
tft.print(L_Month);
L_Month = MONTH;
tft.setTextColor(L_BLUE);
tft.setCursor(56, 86);
if (L_Month < 10) {
tft.print("0");
}
tft.print(L_Month);
}
void Temp_Print() { // Print Temperature
tft.setTextColor(BROWN);
tft.setCursor(120, 60);
if (L_Temp < 10) {
tft.print("0");
}
tft.print(L_Temp);
L_Temp = Temp;
tft.setTextColor(YELLOW);
tft.setCursor(120, 60);
if (L_Temp < 10) {
tft.print("0");
}
tft.print(L_Temp);
}
void Year_Print() { // Print the Year
tft.setTextColor(BLACK);
tft.setCursor(91, 86);
tft.print(YEAR);
tft.print(L_Year);
tft.setTextColor(L_BLUE);
tft.setCursor(91, 86);
tft.print(YEAR);
L_Year = YEAR_2;
tft.print(L_Year);
}
void chime() {
myDFPlayer.volume(15);
switch (Hour2) {
case 1:
myDFPlayer.play(13);
break;
case 2:
myDFPlayer.play(14);
break;
case 3:
myDFPlayer.play(15);
break;
case 4:
myDFPlayer.play(16);
break;
case 5:
myDFPlayer.play(17);
break;
case 6:
myDFPlayer.play(6);
break;
case 7:
myDFPlayer.play(7);
break;
case 8:
myDFPlayer.play(8);
break;
case 9:
myDFPlayer.play(9);
break;
case 10:
myDFPlayer.play(10);
break;
case 11:
myDFPlayer.play(11);
break;
case 12:
myDFPlayer.play(12);
break;
case 13:
myDFPlayer.play(1);
break;
case 14:
myDFPlayer.play(2);
break;
case 15:
myDFPlayer.play(3);
break;
case 16:
myDFPlayer.play(4);
break;
case 17:
myDFPlayer.play(5);
break;
case 18:
myDFPlayer.play(6);
break;
case 19:
myDFPlayer.play(7);
break;
case 20:
myDFPlayer.play(8);
break;
case 21:
myDFPlayer.play(9);
break;
case 22:
myDFPlayer.play(22);
break;
case 23:
myDFPlayer.play(23);
break;
case 24:
myDFPlayer.play(24);
break;
}
}
Comments
Please log in or sign up to comment.