Project Overview:
I am building an automated bell system using an ESP32, an RTC DS1307, a MAX7219 display, and a solid-state relay (HW-883) to control a 220V Edison Bell. The system is powered by an LM2596 step-down module supplying 5V and 3.3V.
Circuit Setup:
- ESP32 powered by 3.3V step-down module
- MAX7219 dot matrix display connected to ESP32
- RTC DS3231 for real-time clock functionality
- Solid-state relay (HW-883) switching a 220V Edison Bell
- LM2596 modules providing 5V for display & relay, 3.3V for ESP32
Issue Faced:
The bell system starts working fine, but after some time, it starts triggering
unexpectedly. However, the relay is OFF when this happens, meaning the bell is activating without the relay being triggered by the ESP32.
What I Have Checked So Far:
The relay module is NOT turning ON when the bell triggers unexpectedly.
The ESP32 is still running and does not freeze or reboot when this happens.
The circuit works fine initially, but after some time, the bell starts activating on its own.
Possible Causes & Questions:
- Back EMF or Inductive Coupling?
- Could the 220V bell be inducing voltage spikes that are somehow affecting the relay module or triggering the bell circuit?
- Would adding a MOV (Metal Oxide Varistor) or RC snubber across the bell terminals help?
- Solid-State Relay (SSR) Leakage?
- Some solid-state relays have leakage current that could be partially triggering the bell.
- Should I test with a mechanical relay instead of SSR to rule this out?
- Power Supply Stability Issues?
- Since Iām using multiple LM2596 step-down converters, could there be power fluctuations affecting the system?
- Would adding a large capacitor (1000µF) near ESP32 VCC help stabilize power?
Questions:
- What could be causing the bell to trigger when the relay is OFF?
- How can I better protect the ESP32 and relay circuit from unwanted activation?
- Would adding an optocoupler or using a mechanical relay instead of SSR help?
type or paste code here
```#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>
#include <Wire.h>
#include <RTClib.h>
#include "esp_task_wdt.h"
//-----------------------
// Hardware & Pin Setup
//-----------------------
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4
// MAX7219 Display pins for ESP32
#define CLK_PIN 18 // Clock
#define DATA_PIN 23 // Data (MOSI)
#define CS_PIN 5 // Chip Select
// Relay control pin (inverted logic: LOW = ON, HIGH = OFF)
#define RELAY_PIN 4
// Watchdog Timer Timeout in seconds
#define WDT_TIMEOUT 10
//-----------------------
// Period Schedule (HH:MM)
//-----------------------
// For periods 1, 6, 7, and 10 the relay is ON for 10 sec; for others, 6 sec.
int periodStartTimes[10][2] = {
{9, 0}, // Period 1
{9, 15}, // Period 2
{10, 15}, // Period 3
{11, 15}, // Period 4
{12, 15}, // Period 5
{13, 15}, // Period 6
{13, 45}, // Period 7
{14, 35}, // Period 8
{15, 25}, // Period 9
{16, 15} // Period 10
};
//-----------------------
// Global Objects & Variables
//-----------------------
MD_Parola display = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);
RTC_DS3231 rtc;
bool blinkColon = true;
unsigned long prevMillis = 0;
const unsigned long blinkInterval = 1000; // Blink colon every 1 second
// Relay timing variables
unsigned long relayStartTime = 0;
bool relayActive = false;
int relayDuration = 6 * 1000; // Default 6 seconds
//-----------------------
// Function: setRTC()
// Allows manual RTC setting via Serial Monitor (format: YYYY MM DD HH MM SS)
void setRTC() {
Serial.println("Enter Date & Time (YYYY MM DD HH MM SS):");
int values[6] = {0}; // Array to store year, month, day, hour, minute, second
int index = 0; // Index for the values array
String input = ""; // String to store serial input
// Wait for input in a non-blocking way
unsigned long startTime = millis();
while (index < 6) {
if (Serial.available() > 0) {
char c = Serial.read();
if (c == '\n' || c == ' ') {
if (input.length() > 0) {
values[index++] = input.toInt(); // Store the parsed integer
input = ""; // Reset the input string
}
} else {
input += c; // Append the character to the input string
}
}
// Feed the watchdog timer to prevent reset
esp_task_wdt_reset();
// Timeout after 30 seconds
if (millis() - startTime > 30000) {
Serial.println("Input timeout! Please try again.");
return;
}
}
// Validate input
if (values[0] < 2000 || values[1] < 1 || values[1] > 12 || values[2] < 1 || values[2] > 31 ||
values[3] < 0 || values[3] > 23 || values[4] < 0 || values[4] > 59 || values[5] < 0 || values[5] > 59) {
Serial.println("Invalid input! Please try again.");
return;
}
// Set the RTC
rtc.adjust(DateTime(values[0], values[1], values[2], values[3], values[4], values[5]));
Serial.println("RTC updated successfully!");
}
//-----------------------
// Function: triggerRelay()
// Activates the relay for the specified duration (in ms) and prints status.
void triggerRelay(int duration) {
if (!relayActive) {
digitalWrite(RELAY_PIN, LOW); // Activate relay (inverted logic)
relayStartTime = millis();
relayDuration = duration;
relayActive = true;
Serial.println("Relay ON");
}
}
//-----------------------
// Task: relayControlTask (Core 1)
// Monitors the RTC and controls relay activation.
void relayControlTask(void *parameter) {
// Register this task with the watchdog timer.
esp_task_wdt_add(NULL);
static int lastPeriod = -1;
for (;;) {
DateTime now = rtc.now();
// Check scheduled periods (prevent duplicate trigger within the same minute)
for (int i = 0; i < 10; i++) {
if (now.hour() == periodStartTimes[i][0] &&
now.minute() == periodStartTimes[i][1] &&
i != lastPeriod) {
int duration = (i == 0 || i == 5 || i == 6 || i == 9) ? 10 * 1000 : 6 * 1000;
triggerRelay(duration);
lastPeriod = i;
break;
}
}
// Turn off relay after its duration
if (relayActive && (millis() - relayStartTime >= relayDuration)) {
digitalWrite(RELAY_PIN, HIGH); // Deactivate relay
relayActive = false;
Serial.println("Relay OFF");
}
esp_task_wdt_reset(); // Feed watchdog
delay(10);
}
}
//-----------------------
// Task: displayTask (Core 0)
// Handles display updates and RTC reading.
void displayTask(void *parameter) {
// Register this task with the watchdog timer.
esp_task_wdt_add(NULL);
static char lastTimeBuffer[6] = "";
for (;;) {
DateTime now = rtc.now();
// Convert to 12-hour format
int displayHour = now.hour();
if (displayHour == 0) displayHour = 12;
else if (displayHour > 12) displayHour -= 12;
// Blink colon every second
unsigned long currentMillis = millis();
if (currentMillis - prevMillis >= blinkInterval) {
prevMillis = currentMillis;
blinkColon = !blinkColon;
}
// Format time string with blinking colon
char timeBuffer[6];
if (blinkColon)
snprintf(timeBuffer, sizeof(timeBuffer), "%2d:%02d", displayHour, now.minute());
else
snprintf(timeBuffer, sizeof(timeBuffer), "%2d %02d", displayHour, now.minute());
// Update display only if time has changed
if (strcmp(timeBuffer, lastTimeBuffer) != 0) {
strncpy(lastTimeBuffer, timeBuffer, sizeof(lastTimeBuffer));
display.displayReset();
display.displayText(timeBuffer, PA_CENTER, 100, 0, PA_PRINT, PA_NO_EFFECT);
} else {
display.displayAnimate();
}
esp_task_wdt_reset(); // Feed watchdog
delay(100);
}
}
//-----------------------
// setup()
// Initialize Serial, peripherals, and create tasks.
void setup() {
Serial.begin(115200);
// Log that we're using the existing TWDT
Serial.println("Using existing Task Watchdog Timer (TWDT).");
// Register main loop task with watchdog.
esp_task_wdt_add(NULL);
// Initialize Display
display.begin();
display.setIntensity(2);
display.displayClear();
// Initialize RTC
Wire.begin();
if (!rtc.begin()) {
Serial.println("RTC NOT FOUND!");
while (1);
}
Serial.println("Enter 'SET' to update RTC manually.");
// Set relay pin
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, HIGH); // Ensure relay is off
// Create tasks on separate cores
xTaskCreatePinnedToCore(displayTask, "DisplayTask", 4096, NULL, 1, NULL, 0); // Core 0
xTaskCreatePinnedToCore(relayControlTask, "RelayTask", 2048, NULL, 1, NULL, 1); // Core 1
}
//-----------------------
// loop()
// Main loop monitors Serial for RTC set command.
void loop() {
if (Serial.available()) {
String command = Serial.readString();
command.trim();
if (command == "SET") {
setRTC();
Serial.println("RTC updated. Enter 'RUN' to resume.");
}
}
// Feed the watchdog timer in the main loop
esp_task_wdt_reset();
delay(10);
}

