Add to cart
Board Type : GerberFile :
Layer : Dimensions :
PCB Qty :
Different PCB Design
PCB Thickness : PCB Color :
Surface Finish : Castellated Hole :
Copper Weight : 1 oz Production Time :
Total: US $
Elecrow community is a shared platform and we are not responsible for any design issues.

PCB Assembly

PCBA Qty: BomFile:
NO. OF UNIQUE PARTS: NO. of Components:
Assembly Cost: US $
Elecrow community is a shared platform and we are not responsible for any design issues.
Add to cart
3dPrintingFile : Size :
Unit : Volumn :
3D Printing Qty : Material :
Total: US $12.99
Elecrow community is a shared platform and we are not responsible for any design issues.
Add to cart
Acrylic Type : AcrylicFile :
Dimensions: Engrave:
Acrylic Qty :
Acrylic Thickness:
Acrylic Color:
Total: US $12.99
Elecrow community is a shared platform and we are not responsible for any design issues.
Add to cart
CNC Milling File : Size:
Unit: Volumn:
CNC Milling Qty : Material:
Type of Aluminum: Surface Finish:
Tolerance:
Surface Roughness:
Total: US $12.99
Elecrow community is a shared platform and we are not responsible for any design issues.
closed
user-img

Stefan Maetschke

Published: Oct 05,2024

Digital Timer Switch with CrowPanel 3.5" Display

Build a Digital Timer Switch that allows you to switch devices on or off on a specified schedule, e.g. your garden sprinkler or lights in your home. This Digital Timer Switch will always have accurate time since it synchronizes its clock with an internet time server. No need to adjust the clock for daylight savings time or to set it after a power outage. Furthermore, the large display makes it easy the set or change schedules.

ccc ccc
Copyright Info

Attribution-ShareAlike (CC BY-SA)

Read More⇒
  906
  4
  1
cover-img
 
  • thumbnail-img
  • thumbnail-img
  • thumbnail-img
  • thumbnail-img
 

Hardware Components

  • CrowPanel 3.5" ESP32 Display

    Elecrow
    X 1 fenxiang

Tools, APP Software Used etc.

  • Arduino IDE

    Arduino IDE

    Arduino

Story

In this tutorial you will learn how to build a Digital Timer Switch with the CrowPanel 3.5" ESP32 Display. We are going to synchronizing the internal clock with an internet time provider to keep it accurate and use the TFT_eSPI library to build a nice UI that makes it easy to set times. See the demo below.

This image has an empty alt attribute; its file name is digital-timer-switch-ui-demo.gif

A Digital Timer Switch is an electronic device that automatically turns other devices on or off at specific times set by the user. It typically has a digital display for programming on/off times. It can be used to control lights, appliances, and other electrical devices at predetermined times. For example, you can use it to turn on lights in your home at sunset and turn them off at bedtime, or to schedule your sprinkler to water your lawn, and then turn them off after a specific duration to avoid overwatering.

The Digital Timer Switch you will build in this tutorial will be better than most commercial digital timer switches. It's time will be always accurate and there will be no need to adjust for daylight savings time. Furthermore, due to the large display it will be easy to set or change schedules.

Create Project Structure

Before going into any details, let's create the project structure and the setup for the TFT_eSPI library.

Open your Arduino IDE and create a project "digital_timer_switch" and save it (Save As ...). This will create a folder "digital_timer_switch" with the file "digital_timer_switch.ino" in it. In this folder create another file named "datestrs.h" and a third named "tft_setup.h". Your project folder should look like this

Project Folder Structure

and in your Arduino IDE you should now have three tabs named "digital_timer_switch.ino", "datestrs.h" and "tft_setup.h".

Arduino IDE with three Tabs
Arduino IDE with three Tabs

Click on the "tft_setup.h" tab to open the file and copy the following code into it:

#define ILI9488_DRIVER
#define TFT_HEIGHT  480
#define TFT_WIDTH   320 

#define TFT_BACKLIGHT_ON HIGH
#define TFT_BL   27 
#define TFT_MISO 12
#define TFT_MOSI 13
#define TFT_SCLK 14
#define TFT_CS   15
#define TFT_DC    2 
#define TFT_RST  -1
#define TOUCH_CS 33

#define SPI_FREQUENCY        27000000
#define SPI_TOUCH_FREQUENCY   2500000
#define SPI_READ_FREQUENCY   16000000

#define LOAD_GLCD   // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
#define LOAD_FONT2  // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
#define LOAD_FONT4  // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
#define LOAD_FONT6  // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
#define LOAD_FONT7  // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-.
#define LOAD_FONT8  // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
#define LOAD_GFXFF  // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts

This code tells the TFT_eSPI library, which display are we using. Specifically, we let the library know the dimensions of the display (TFT_WIDTH, TFT_WIDTH), its display driver (ILI9488_DRIVER), which SPI pins are used to control it and what fonts to load. Without these settings, you will not be able to show anything on the display.

For more detailed information have a look at the tutorial CrowPanel 2.8" ESP32 Display : Easy Setup Guide. It explains how to setup the 2.8" Display. But the steps and descriptions apply to the 3.5" Display in the same way. Just the settings for the screen dimensions and driver are different.

Calibrating Touchscreen

The CrowPanel 3.5" Display comes with a resistive touch screen that you need to calibrate first, before you can used it with the TFT_eSPI library. Copy the code below into the "digital_timer_switch.ino" file, compile it and upload it to the CrowPanel 3.5" Display.

#include "tft_setup.h"
#include "TFT_eSPI.h"

TFT_eSPI tft = TFT_eSPI();

void setup() {
  Serial.begin(115200);
  tft.begin();
  tft.setRotation(0);
}

void loop() {
  uint16_t cal[5];
  tft.fillScreen(TFT_BLACK);
  tft.setCursor(20, 0);
  tft.setTextFont(2);
  tft.setTextSize(1);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.print("Touch corners ... ");
  tft.calibrateTouch(cal, TFT_MAGENTA, TFT_BLACK, 15);
  tft.println("done.");

  Serial.printf("cal: {%d, %d, %d, %d, %d}\n",
                cal[0], cal[1], cal[2], cal[3], cal[4]);
  delay(10000);
}

When the code is running the displays shows an arrow and tells you to touch the corner it points to. This will be repeated for the other three corners as well. Use the little pen that comes with Display to do this and try to touch the corners as precisely as possible.

Calibration of touch screen
Calibration of touch screen

At the end of the calibration process the code prints out the 5 calibration parameters (corner coordinates and screen orientation) to the Serial monitor. You should see something like this:

cal: { 260, 3518, 270, 3629, 4 }

Copy the parameters somewhere, since you will need them in the digital timer switch code. The calibration repeats itself every 10 seconds, so you can have several tries to get the most accurate parameters.

For more detailed information on the calibration process have a look at the CrowPanel 2.8" ESP32 Display : Easy Setup Guide tutorial.

Code for Digital Timer Switch

In this section we will write the complete code for the Digital Timer Switch. Start by copying the following code into the "datestrs.h" file that is in your digital_timer_switch project folder.

// datestrs.h
const char *DAYSTR[] = {
    "Su",
    "Mo",
    "Tu",
    "We",
    "Th",
    "Fr",
    "Sa"
};

const char *MONTHSTR[] = {
    "Jan",
    "Feb",
    "Mar",
    "Apr",
    "May",
    "Jun",
    "Jul",
    "Aug",
    "Sep",
    "Oct",
    "Nov",
    "Dec"
};

Next copy all of the code below into the "digital_timer_switch.ino" file and replace the calibration code in there.

This is the main code. It allows users to set alarms based on a weekly schedule, view the current time, and toggle alarms on and off. The display updates in real-time, and the user can interact with it through touch buttons. Just have a quick read and then we will discuss the details of the code.

// digital_timer_switch.ino
#include "tft_setup.h"
#include "TFT_eSPI.h"
#include "TFT_eWidget.h"
#include "stdarg.h"
#include "WiFi.h"
#include "esp_sntp.h"
#include "datestrs.h"

const char* TIMEZONE = "AEST-10AEDT,M10.1.0,M4.1.0/3";
const char* SSID = "SSID";
const char* PWD = "PASSWORD";

const int rs = 24, cs = 7;
const int mx = 60, my = 40;
const int gw = TFT_WIDTH - mx - 30;
const int gh = TFT_HEIGHT - my - 50;
const int cw = gw / cs;
const int ch = gh / rs;

const int alarmPin = 25;

bool schedule[rs][cs];
bool isTimeView = true;
bool isAlarmOn = false;
uint16_t cal[5] = { 260, 3518, 270, 3629, 4 };

TFT_eSPI tft = TFT_eSPI();
ButtonWidget btnView = ButtonWidget(&tft);
ButtonWidget btnAlarm = ButtonWidget(&tft);
ButtonWidget* btns[] = { &btnView, &btnAlarm };


int c2x(int c) {
  return mx + gw * c / cs;
}

int r2y(int r) {
  return my + gh * r / rs;
}

int x2c(int x) {
  return map(x - mx, 0, gw, 0, cs);
}

int y2r(int y) {
  return map(y - my, 0, gh, 0, rs);
}

void drawLabels() {
  tft.setTextFont(1);
  tft.setTextSize(2);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  for (int r = 0; r < rs; r++) {
    tft.setCursor(24, r2y(r));
    tft.printf("%02d", r);
  }
  for (int c = 0; c < cs; c++) {
    tft.setCursor(c2x(c), 15);
    tft.print(DAYSTR[c]);
  }
}

void drawSlot(int r, int c) {
  uint32_t color = schedule[r][c] ? TFT_WHITE : TFT_BLACK;
  tft.fillRect(c2x(c) + 1, r2y(r) + 1, cw - 2, ch - 2, color);
}

void drawSchedule() {
  for (int r = 0; r < rs; r++) {
    for (int c = 0; c < cs; c++) {
      tft.drawRect(c2x(c), r2y(r), cw, ch, TFT_DARKGREY);
      drawSlot(r, c);
    }
  }
}

void selectSlot(int x, int y) {
  int c = x2c(x);
  int r = y2r(y);
  if (c < 0 || c >= cs || r < 0 || r >= rs) return;
  schedule[r][c] = !schedule[r][c];
  drawSlot(r, c);
}

void initWiFi() {
  WiFi.begin(SSID, PWD);
  while (WiFi.status() != WL_CONNECTED) {
    delay(100);
  }
}

void initSNTP() {
  sntp_set_sync_interval(15 * 60 * 1000UL);  // 15 minutes
  esp_sntp_setoperatingmode(ESP_SNTP_OPMODE_POLL);
  esp_sntp_setservername(0, "pool.ntp.org");
  esp_sntp_init();
  setTimezone();
}

void setTimezone() {
  setenv("TZ", TIMEZONE, 1);
  tzset();
}

void drawTime() {
  static char buff[50];
  static struct tm t;
  getLocalTime(&t);

  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.setTextDatum(MC_DATUM);
  tft.setTextSize(1);

  sprintf(buff, " %2d:%02d:%02d ", t.tm_hour, t.tm_min, t.tm_sec);
  tft.drawString(buff, tft.width() / 2, 130, 7);

  sprintf(buff, "  %s, %s %d, %d ",
          DAYSTR[t.tm_wday], MONTHSTR[t.tm_mon],
          t.tm_mday, t.tm_year + 1900);
  tft.drawString(buff, tft.width() / 2, 200, 4);
}

bool isOn() {
  static struct tm t;
  getLocalTime(&t);
  return schedule[t.tm_hour][t.tm_wday] && isAlarmOn;
}

void initDisplay() {
  tft.begin();
  tft.setRotation(0);
  tft.fillScreen(TFT_BLACK);
  tft.setTouch(cal);
}

void btnView_pressed(void) {
  if (btnView.justPressed()) {
    isTimeView = !btnView.getState();
    drawView();
    tft.setTextFont(4);
    tft.setTextSize(1);
    btnView.drawSmoothButton(isTimeView, 1, TFT_BLACK, isTimeView ? "alarm" : "time");
  }
}

void btnAlarm_pressed(void) {
  if (btnAlarm.justPressed()) {
    isAlarmOn = !btnAlarm.getState();
    tft.setTextFont(4);
    tft.setTextSize(1);
    btnAlarm.drawSmoothButton(isAlarmOn, 1, TFT_DARKGREY, isAlarmOn ? "on" : "off");
  }
}

void initButtons() {
  uint16_t w = 100;
  uint16_t h = 50;
  uint16_t y = tft.height() - h + 14;
  uint16_t x = tft.width() / 2;
  tft.setTextFont(4);
  tft.setTextSize(1);

  btnView.initButtonUL(x - w - 10, y, w, h, TFT_DARKGREY, TFT_DARKGREY, TFT_BLACK, "alarm", 1);
  btnView.setPressAction(btnView_pressed);
  btnView.drawSmoothButton(isTimeView, 1, TFT_BLACK);

  btnAlarm.initButtonUL(x + 10, y, w, h, TFT_DARKGREY, TFT_BLACK, TFT_DARKGREY, "off", 1);
  btnAlarm.setPressAction(btnAlarm_pressed);
  btnAlarm.drawSmoothButton(isAlarmOn, 1, TFT_BLACK);
}

void handleButtons() {
  tft.setTextFont(4);
  uint8_t nBtns = sizeof(btns) / sizeof(btns[0]);
  uint16_t x = 0, y = 0;
  bool touched = tft.getTouch(&x, &y);
  for (uint8_t b = 0; b < nBtns; b++) {
    if (touched) {
      if (btns[b]->contains(x, y)) {
        btns[b]->press(true);
        btns[b]->pressAction();
      }
    } else {
      btns[b]->press(false);
      btns[b]->releaseAction();
    }
  }
}

void drawView() {
  tft.fillScreen(TFT_BLACK);
  initButtons();
  if (isTimeView) {
    drawTime();
  } else {
    drawSchedule();
    drawLabels();
  }
}

void updateView() {
  static uint16_t x = 0, y = 0;
  if (isTimeView) {
    drawTime();
  } else {
    if (tft.getTouch(&x, &y)) {
      selectSlot(x, y);
    }
  }
}

void setup() {
  initWiFi();
  initSNTP();
  initDisplay();
  pinMode(alarmPin, OUTPUT);
  drawView();
}

void loop() {
  updateView();
  handleButtons();
  digitalWrite(alarmPin, isOn() ? HIGH : LOW);
  delay(100);
}

As you can see, this is quite a bit of code. The following section provides you with a quick overview of the functions and their meaning.

Functions Overview

- drawLabels(): Draws labels for the rows and columns on the display.

- drawSlot(int r, int c): Draws an hour slot on the schedule grid based on the provided row and column.

- drawSchedule(): Draws the entire schedule grid on the display.

- selectSlot(int x, int y): Allows users to select an hour slot on the schedule grid by touching the display.

- initWiFi(): Initializes the WiFi connection using the provided SSID and password.

- initSNTP(): Initializes the SNTP (Simple Network Time Protocol) for time synchronization.

- setTimezone(): Sets the timezone for the device based on the provided constant.

- drawTime(): Draws the current time and date on the display.

- isOn(): Checks if the current time corresponds to a scheduled slot and the alarm is enabled.

- initDisplay(): Initializes the display and sets up touch functionality.

- btnView_pressed() and btnAlarm_pressed(): Handle button press events for switching between time view and schedule view, and toggling the alarm status.

- initButtons(): Initializes the buttons on the display for interacting with the project.

- handleButtons(): Handles button press and release actions on the display.

- drawView(): Draws either the time view or the schedule view based on the current mode.

- updateView(): Updates the display based on user interactions and current mode.

Let's have a closer look at these functions and other parts of the code.

 

Library Inclusions

The code begins by including several libraries necessary for the functionality of the ESP32 and the display. We need libraries for the TFT display, the Wi-Fi connection, the time synchronization with an SNTP time server and the names of months and days (datestrsl.h)

#include "tft_setup.h"
#include "TFT_eSPI.h"
#include "TFT_eWidget.h"
#include "stdarg.h"
#include "WiFi.h"
#include "esp_sntp.h"
#include "datestrs.h"

 

Constants and Variables

Next, we define several constants and variables that will be used throughout the program. These include Wi-Fi credentials, display dimensions, alarm pin, and a schedule array.

const char* TIMEZONE = "AEST-10AEDT,M10.1.0,M4.1.0/3";
const char* SSID = "SSID";
const char* PWD = "PASSWORD";

const int rs = 24, cs = 7;
const int alarmPin = 25;

bool schedule[rs][cs];
bool isTimeView = true;
bool isAlarmOn = false;

TIMEZONE. The above time zone specification "AEST-10AEDT,M10.1.0,M4.1.0/3" is for Australia, which corresponds to the Australian Eastern Standard Time (AEST) with daylight saving time adjustments.

The parts of this time zone definition are as follows

AEST: Australian Eastern Standard Time
-10: UTC offset of 10 hours ahead of Coordinated Universal Time (UTC)
AEDT: Australian Eastern Daylight Time
M10.1.0: Transition to daylight saving time occurs on the 1st Sunday of October
M4.1.0/3: Transition back to standard time occurs on the 1st Sunday of April, with a 3-hour difference from UTC.

For other time zone definitions have a look at the Posix Timezones Database. Just copy and paste the string you find there and change the TIMEZONE constant accordingly.

 

Coordinate Conversion Functions

The following functions convert between coordinate systems for the display:

int c2x(int c) { ... }
int r2y(int r) { ... }
int x2c(int x) { ... }
int y2r(int y) { ... }

 

Drawing Functions

The code includes several functions to draw elements on the display:

drawLabels

This function draws the labels for the rows (hours) and columns (week days) of the schedule.

void drawLabels() {
  tft.setTextFont(1);
  tft.setTextSize(2);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  for (int r = 0; r < rs; r++) {
    tft.setCursor(24, r2y(r));
    tft.printf("%02d", r);
  }
  for (int c = 0; c < cs; c++) {
    tft.setCursor(c2x(c), 15);
    tft.print(DAYSTR[c]);
  }
}

drawSlot

This function draws a single hour time slot in the schedule, filling it with white if the alarm is set for that time.

void drawSlot(int r, int c) {
  uint32_t color = schedule[r][c] ? TFT_WHITE : TFT_BLACK;
  tft.fillRect(c2x(c) + 1, r2y(r) + 1, cw - 2, ch - 2, color);
}

drawSchedule

This function draws the entire schedule grid, calling drawSlot() for each time slot.

void drawSchedule() {
  for (int r = 0; r < rs; r++) {
    for (int c = 0; c < cs; c++) {
      tft.drawRect(c2x(c), r2y(r), cw, ch, TFT_DARKGREY);
      drawSlot(r, c);
    }
  }
}

Hourly grid for Schedule
Hourly grid for Schedule

selectSlot

This function toggles the state and color of a time slot when the user touches it on the display.

void selectSlot(int x, int y) {
  int c = x2c(x);
  int r = y2r(y);
  if (c < 0 || c >= cs || r < 0 || r >= rs) return;
  schedule[r][c] = !schedule[r][c];
  drawSlot(r, c);
}

 

Wi-Fi and SNTP Initialization

The following functions handle the initialization of Wi-Fi and SNTP (Simple Network Time Protocol):

initWiFi

This function connects the ESP32 to the specified Wi-Fi network.

void initWiFi() {
  WiFi.begin(SSID, PWD);
  while (WiFi.status() != WL_CONNECTED) {
    delay(100);
  }
}

initSNTP

This function sets up the SNTP service to synchronize the time. It essentially connects to a server on the internet that provides very accurate time information and synchronizes the internal clock of the ESP32 accordingly.

void initSNTP() {
  sntp_set_sync_interval(15 * 60 * 1000UL);  // 15 minutes
  esp_sntp_setoperatingmode(ESP_SNTP_OPMODE_POLL);
  esp_sntp_setservername(0, "pool.ntp.org");
  esp_sntp_init();
  setTimezone();
}

As long as there is a Wi-Fi and internet connection, this ensures that the clock of our digital timer switch is always accurate and automatically adjusts to daylight savings time. For more details have a look at the How to synchronize ESP32 clock with SNTP server tutorial.

setTimezone

This function sets the timezone for the application. You have to make sure that this functions runs every time the ESP32 starts, otherwise your clock will be off.

void setTimezone() {
  setenv("TZ", TIMEZONE, 1);
  tzset();
}

 

Time Display Function

The drawTime() function retrieves the current local time, which is synchronized via SNTP, and displays the time and date on the screen.

void drawTime() {
  static char buff[50];
  static struct tm t;
  getLocalTime(&t);

  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.setTextDatum(MC_DATUM);
  tft.setTextSize(1);

  sprintf(buff, " %2d:%02d:%02d ", t.tm_hour, t.tm_min, t.tm_sec);
  tft.drawString(buff, tft.width() / 2, 130, 7);

  sprintf(buff, "  %s, %s %d, %d ",
          DAYSTR[t.tm_wday], MONTHSTR[t.tm_mon],
          t.tm_mday, t.tm_year + 1900);
  tft.drawString(buff, tft.width() / 2, 200, 4);
}

The function uses the constants for the names of days and months stored in the datestrs.h file.


Time and Date shown on Display
Time and Date shown on Display

If you need more details have a look at the Digital Clock with CrowPanel 3.5″ ESP32 Display and the Digital Clock on e-Paper Display tutorials, which also implement digital clocks.

 

Alarm Check Function

The isOn() function checks if the alarm should be triggered based on the current time and schedule. If the grid cell schedule[t.tm_hour][t.tm_wday] contains true and the overall alarm switch is on (isAlarmOn), the function returns true and otherwise false.

bool isOn() {
  static struct tm t;
  getLocalTime(&t);
  return schedule[t.tm_hour][t.tm_wday] && isAlarmOn;
}

 

Display Initialization

The initDisplay() function initializes the display by setting the screen rotation, filling the screen with black, setting the calibration parameter for the touch screen.

void initDisplay() {
  tft.begin();
  tft.setRotation(0);
  tft.fillScreen(TFT_BLACK);
  tft.setTouch(cal);
}

 

Button Functions

The code includes functions to handle button presses for toggling between time view and alarm settings:

btnView_pressed

This function toggles the view between the current time and the alarm schedule.

void btnView_pressed(void) {
  if (btnView.justPressed()) {
    isTimeView = !btnView.getState();
    drawView();
    tft.setTextFont(4);
    tft.setTextSize(1);
    btnView.drawSmoothButton(isTimeView, 1, TFT_BLACK, isTimeView ? "alarm" : "time");
  }
}

Toggling time and schedule view
Toggling time and schedule view

btnAlarm_pressed

This function toggles the overall alarm switch on and off. If the switch is off the alarm schedule is disabled.

void btnAlarm_pressed(void) {
  if (btnAlarm.justPressed()) {
    isAlarmOn = !btnAlarm.getState();
    tft.setTextFont(4);
    tft.setTextSize(1);
    btnAlarm.drawSmoothButton(isAlarmOn, 1, TFT_DARKGREY, isAlarmOn ? "on" : "off");
  }
}

Toggling of alarm switch

 

Button Initialization

The initButtons() function initializes the buttons on the display.

void initButtons() {
  uint16_t w = 100;
  uint16_t h = 50;
  uint16_t y = tft.height() - h + 14;
  uint16_t x = tft.width() / 2;
  tft.setTextFont(4);
  tft.setTextSize(1);

  btnView.initButtonUL(x - w - 10, y, w, h, TFT_DARKGREY, TFT_DARKGREY, TFT_BLACK, "alarm", 1);
  btnView.setPressAction(btnView_pressed);
  btnView.drawSmoothButton(isTimeView, 1, TFT_BLACK);

  btnAlarm.initButtonUL(x + 10, y, w, h, TFT_DARKGREY, TFT_BLACK, TFT_DARKGREY, "off", 1);
  btnAlarm.setPressAction(btnAlarm_pressed);
  btnAlarm.drawSmoothButton(isAlarmOn, 1, TFT_BLACK);
}

Buttons for View and Alarm
Buttons for View and Alarm

 

Button Handling

The handleButtons() function checks for button presses and executes the corresponding actions.

void handleButtons() {
  tft.setTextFont(4);
  uint8_t nBtns = sizeof(btns) / sizeof(btns[0]);
  uint16_t x = 0, y = 0;
  bool touched = tft.getTouch(&x, &y);
  for (uint8_t b = 0; b < nBtns; b++) {
    if (touched) {
      if (btns[b]->contains(x, y)) {
        btns[b]->press(true);
        btns[b]->pressAction();
      }
    } else {
      btns[b]->press(false);
      btns[b]->releaseAction();
    }
  }
}

 

View Drawing Function

The drawView() function updates the display based on the current view mode (time or schedule).

void drawView() {
  tft.fillScreen(TFT_BLACK);
  initButtons();
  if (isTimeView) {
    drawTime();
  } else {
    drawSchedule();
    drawLabels();
  }
}

 

View Update Function

The updateView() function refreshes the display based on user interaction.

void updateView() {
  static uint16_t x = 0, y = 0;
  if (isTimeView) {
    drawTime();
  } else {
    if (tft.getTouch(&x, &y)) {
      selectSlot(x, y);
    }
  }
}

 

Setup Function

The setup() function initializes the Wi-Fi, SNTP, display, and switches GPIO25 into output mode. We will connect an LED or an Relay to this pin and control them via the alarm schedule.

void setup() {
  initWiFi();
  initSNTP();
  initDisplay();
  pinMode(alarmPin, OUTPUT);
  drawView();
}

 

Loop Function

Finally, the loop() function continuously updates the view, handles button presses, and controls the alarm pin based on the schedule.

void loop() {
  updateView();
  handleButtons();
  digitalWrite(alarmPin, isOn() ? HIGH : LOW);
  delay(100);
}

And this is the complete code for a digital timer switch. In the next section, I show you how to connect a Relay to the Crowpanel Display, which we then can control via the alarm schedule.

 

Connecting a Relay to the Digital Timer Switch

If you want to control lights in your home or the pump of you sprinkler you will need a relay. The following diagram shows you how to wire that up:

Relay connected to CrowPanel 3.5" ESP32 Display

VCC (red cable) and GND (black cable) are connected to the corresponding pins on a relay module. GPIO25 is connected to the input pin (yellow cable) of the relay.

The high voltage, high current device you want to control is connected to the COM and NC (normal open) terminals of the relay module. Be very careful when operating with voltages higher than 50V ! You must make sure that the relay module is rated for the voltage and current you want to switch. Typical relay modules (AITRIP, HiLetgo) can switch 220V at 4A up to 10A.

The photo below shows the wiring of the relay module with the CrowPanel Display

Wiring of Relay with Crowpanel Display
Wiring of Relay with CrowPanel Display

To test the digital timer switch read the current time and date, e.g. 12:37:00 on Wednesday and then set the corresponding time slot the alarm schedule:

Schedule set for 12:00-13:00 on Wednesday
Schedule set for 12:00-13:00 on Wednesday

If the overall alarm button is on, the Relay should be on. You then can switch it off by toggling the overall alarm button or changing the schedule.

 

Conclusion

In this tutorial you learned how to build a Digital Timer Switch with the CrowPanel 3.5" ESP32 Display. Compared to many commercially available products or Digital Timer Switch has always accurate time and due the large display makes it very easy to quickly set and change schedules.

There are many possible extensions to this project. You may want to be able to set schedules with a resolution of minutes or seconds. Or may be you want to program a schedule for an entire month or year.

Instead of connecting a relay you could also use Bluetooth, MQTT or other protocols to control devices wirelessly. The code above gives you a framework for it.

Happy tinkering ;)

 

Here some links that I found helpful for writing this post.

CrowPanel 3.5"-ESP32 Display
CrowPanel ESP32 Display Wiki
CrowPanel ESP32 Display User_Manual
CrowPanel 3.5" Schematic Diagram
CrowPanel ESP32 Display Video Tutorials
TFT 3.5" Touch Screen & ESP32 built in - Elecrow review
Getting Started Tips 3.5" Elecrow TFT ESP32

user-img

Stefan Maetschke

+ Follow

Topic

View All
  • Comments( 1 )
  • Like( 4 )
/1000
Upload a photo:
You can only upload 1 files in total. Each file cannot exceed 2MB. Supports JPG, JPEG, GIF, PNG, BMP
  • Not sure why but this is one of my favorite projects. Nice!
    Oct 07,2024 0 comments
    Reply

You May Also Like

View All