ESP32 Tutorial: Wireless Doorbell Logger

Introduction

This tutorial showcases the construction of a Wireless Doorbell using 2 ESP32 Development Boards. It uses ESP-NOW, a protocol developed by Espressif to allow 2 ESP32s to communicate with each other without the need for WiFi connections.

 

Hardware Required

You may purchase the individual components here:

  1. Solderless Breadboard 400 Tie Point
  2. WiFi Bluetooth Module ESP32-WROOM-32D DevKitC
  3. LCD TFT 2.4" Resistive Touch
  4. Button With Cap Module Green
  5. Clock Module RTC DS3231
  6. Active Buzzer Module OEM
  7. Rainbow LED 1x5 Module
  8. Jumper Wire Male-Male 40x

Solderless Breadboard 400 Tie Point

A solderless breadboard is perfect for prototyping. No hassle of soldering wires. All connections can be connected via jumper wires. There is a strip of double sided tape at the bottom of the breadboard so you can fix the breadboard in place.

WiFi Bluetooth Module ESP32-WROOM-32D DevKitC

ESP32-WROOM-32D is a powerful Wi-Fi+ Bluetooth + Bluetooth Low Energy MCU. It is small in size and supports LWIP protocol, FreeRTOS and works in 3 modes: AP, STA, AP + STA. The core of this module is the ESP32-WROOM-32D chip with two CPU cores that can be individually controlled or powered on. The clock frequency can be adjusted from 80MHz to 240MHz. The ESP32-WROOM-32D also integrates a wealth of peripherals, including capacitive touch sensors, hall sensors, low-noise sense amplifiers, SD card interface, ethernet interface, high-speed SDIO / SPI, UART, I2S and I2C.

LCD TFT 2.4" Resistive Touch

Add a display to your project with this 65K/262K colours LCD TFT. This display has 240 x 320 colour pixels, which is really sharp and bright. It uses 4-wire SPI to communicate, which means you can save your microcontroller pins for other uses. This display has a resistive touchscreen attached on it, so you can detect finger presses anywhere on the screen. There is also a SD card holder so you can easily load full color bitmaps from a FAT16/FAT32 formatted card.

Button With Cap Module Green

A button is almost always essential in any project. With this button module, you can now receive inputs to your project. The large surface area makes the button very easy to press. Comes in multiple colours, easy to differentiate different functions!

Clock Module RTC DS3231

This clock module DS3231 RTC is low-cost, extremely accurate I2C real-time clock (RTC), with an integrated temperature-compensated crystal oscillator (TCXO) and crystal. The clock module incorporates a battery input which maintains accurate timekeeping even without a power supply. It has an integrated oscillator to improve long-term accuracy of the device.

Active Buzzer Module OEM

This small active buzzer will give you a long and loud 2kHz beep when you apply a power of 5V. This buzzer does not require an external oscillator as there is a driver that makes it oscillate at 2kHz when powered. The buzzer is driven from a transistor so you can get the full power of the buzzer volume.

Rainbow LED 1x5 Module

This LED Light has an ultra bright smart LED NeoPixel. The LEDs are 'chainable', which means you can connect the output pin of one to the input pin of another. It only makes use of one microcontroller pin to control as many as you can chain together! Each LED is addressable as the driver chip is inside the LED. Each one has ~18mA constant current drive so the color will be very consistent even if the voltage varies, and no external choke resistors are required making the design slim. Power the whole thing with 5VDC (4 - 7V works) and you're ready to rock.

Jumper Wire Male-Male 40x

Jumper wires are essential with any breadboard prototyping. These wires are 20cm long and come in a strip of 40 pieces (4 sets with 10 of each colour). You can always pull the ribbon wires off to make individual jumpers, or keep them together to make neatly organized wire harnesses.

Connecting the Hardware

The doorbell transmitter sends a signal to the doorbell receiver when someone presses the button. Connect one leg of the button to the GND pin of an ESP32 and connect the other leg to pin 23. 

We will begin assembling the doorbell receiver by connecting the LCD TFT module to the ESP32. The LCD TFT module will display the date, time, and the information to say that the doorbell has been pressed. Connect the LCD TFT to the ESP32 as shown in the diagram below:

  • The following pins allow communications between the ESP32 and the LCD TFT Module
    • LCD TFT (SDO MISO) - ESP32 (19)
    • LCD TFT (SCK) - ESP32 (18)
    • LCD TFT (SDI MOSI) - ESP32 (23)
    • LCD TFT (D/C) - ESP32 (2)
    • LCD TFT (RESET) - ESP32 (4)
    • LCD TFT (CS) - ESP32 (15)
  • LCD TFT (LED) - ESP32 (3V3): This powers the LCD screen on the LCD TFT
  • LCD TFT (GND) - ESP32 (GND): This grounds the LCD TFT
  • LCD TFT (VCC) - ESP32 (VCC): This powers the LCD TFT

The Rainbow LED lights up whenever the doorbell receiver receives a signal when the doorbell transmitter's button is pressed. Connect the Rainbow LED to the ESP32 as shown in the diagram below:

  • Rainbow LED (DIN) - ESP32 (25): This allows the ESP32 to send a signal to tell the Rainbow LED to light up
  • Rainbow LED (5V) - ESP32 (3V3): This powers the Rainbow LED
  • Rainbow LED (GND) - ESP32 (GND): This grounds the Rainbow LED

The Clock RTC DS3231 module allows us to log the time that the doorbell transmitter's button is pressed. Connect the Clock module to the ESP32 as shown in the diagram below:

  • Clock Module (SCL) - ESP32 (22): The SCL (Serial Clock) on the clock module will send signals at regular intervals to the ESP32. This allows time intervals to be recorded.
  • Clock Module (SDA) - ESP32 (21): The SDA (data) pin on the clock module allows the Clock Module to communicate by sending data to the ESP32.
  • Clock Module (3V3) - ESP32 (3V3): This powers the Clock Module
  • Clock Module (GND) - ESP32 (GND): This grounds the Clock Module

The Active Buzzer module produces an audible buzz when the doorbell receiver receives a signal to indicate that it has been pressed. Connect the Active Buzzer module to another ESP32 as shown in the diagram below:

  • Active Buzzer (GND) - ESP32 (GND): This grounds the Active Buzzer
  • Active Buzzer (I/O) - ESP32 (14): This allows the Active Buzzer to receive signals sent by the ESP32
  • Active Buzzer (VCC) - ESP32 (3V3): This powers the Active Buzzer

Install the ESP32 Extension for Arduino

Install the ESP32 extension for Arduino. A tutorial on how to install can be found here.

Install the Libraries

There are 3 libraries that are required for this project, namely:

  • TFT_eSPI: Library and driver for the TFT LCD Module
  • RTClib: Library for the Clock Module
  • Adafruit Neopixel: Library for the LED strip

On the Arduino IDE, go to Tools > Manage Libraries and install ”TFT_eSPI”, “RTClib” and “Adafruit Neopixel”.

Modifying the TFT_eSPI Library Setup Files

Open the file Arduino libraries > TFT_eSPI > User_Setup.h. We will be modifying some codes to match our ESP32 setup. Our TFT screen is using ILI9341 driver, so check line 39 to make sure it’s active.

Comment lines 161 to 163 as we are not using ESP8266 setup

Uncomment lines 196 to 201 for our ESP32 pin connections. OK, we are now ready to program our doorbell transmitter and receiver!

Programming The Doorbell Transmitter

We will be using ESP-Now to communicate wirelessly between our Doorbell Transmitter and Receiver. ESP-NOW is a protocol developed by Espressif, which enables multiple devices to communicate with one another without using Wi-Fi, similar to the low-power 2.4GHz wireless connectivity that is often deployed in a wireless mouse.

On the Arduino IDE, select the "ESP32 Dev Module".

Open Examples > ESP32 > ESPNow > Basic > Master.

We will replace the following sample codes from the Master program:

// Global copy of slave
esp_now_peer_info_t slave;
#define CHANNEL 3
#define PRINTSCANRESULTS 0
#define DELETEBEFOREPAIR 0

Replace the MAC address (0x00, 0x00, 0x00, 0x00, 0x00, 0x00) with the MAC address of the ESP32 that you are using for the Doorbell Receiver. The Doorbell Receiver program that we are developing in the next section will display the ESP32's MAC address on the TFT LCD screen.

// REPLACE WITH YOUR RECEIVER MAC Address
uint8_t broadcastAddress[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

Declare a button for the doorbell receiver

// declare pin
const int buttonPin = 23;

Declare the variables that we will be using in the Doorbell Transmitter program.

// declare pin
const int buttonPin = 23;

Declare the variables that we will be using in the Doorbell Transmitter program.

// declare variables
int buttonState, lastButtonState, debounceDelay;
unsigned long lastDebounceTime;
uint8_t data;

Declare an object for ESP-Now so that we can call it later.

// declare object
esp_now_peer_info_t peerInfo;

Delete the methods ScanForSlave(), manageSlave() and deletePeer() as we will not be needing them.

Add a readButton() method with debounce to filter out noise when it is pressed. When the button is pressed, we will call the sendData() method to send a message to our other ESP32 device.

// send message to other ESP32
if (buttonState == LOW) {
// Send message via ESP-NOW
sendData();
}

In our main loop, call method readButton() to detect it when the button is pressed.

void loop() {
readButton();
}

That’s it! Now our doorbell transmitter is ready. We can move on to program our doorbell receiver.

Programming The Doorbell Receiver

On the Arduino IDE, select the "ESP32 Dev Module".

Include the required libraries for the program. “Free_Fonts.h” can be found from Arduino libraries > TFT_eSPI > examples > 320x240 > All_Free_Fonts_Demo. Copy “Free_Fonts.h” and put it into the same folder as your program so your program can find the file to compile.

// include libraries
#include <esp_now.h>
#include <WiFi.h>
#include <TFT_eSPI.h> // Hardware-specific library
#include <SPI.h>
#include "Free_Fonts.h"
#include "RTClib.h"
#include <Adafruit_NeoPixel.h>

We declare a LED_COUNT variable for the number of Neopixels and a BRIGHTNESS variable for the brightness of Neopixel. 150 is a nice balance between brightness and power consumption.

// declare user variables
#define LED_COUNT 5
#define BRIGHTNESS 150 // NeoPixel brightness, 0 (min) to 255 (max)

Next, we declare our pins, variables, and objects. We keep an array of 5 data sets to keep track of the most recent 5 data received from our doorbell transmitter.

// declare pins
const int buzzerPin = 14;
const int neopixelPin = 25;

// declare variables
uint8_t data;
char logArray[5][22];

// declare objects
TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
RTC_DS3231 rtc;
Adafruit_NeoPixel strip(LED_COUNT, neopixelPin, NEO_GRB + NEO_KHZ800);

The drawStartScreen() method will draw the start-up screen on the TFT LCD, with the mac address of the Doorbell Receive displayed at the bottom of the screen. When we run the program, the MAC address of the ESP32 will be displayed on the TFT LCD screen. We may then copy and enter this MAC address into the codes for the Doorbell Transmitter mentioned in the section "Programming the Doorbell Transmitter" above.

void drawStartScreen() {
 tft.setTextColor(TFT_WHITE, TFT_BLACK);
 tft.setFreeFont(FF18);
 tft.drawString("DOORBELL LOGGER", 5, 5, GFXFF);
 tft.drawRect(0, 40, 320, 160, TFT_WHITE);
 tft.setTextColor(TFT_WHITE, TFT_BLACK);
 tft.setFreeFont(FF17);
 tft.drawString("MAC Address :", 5, 220, GFXFF);
 tft.setTextColor(TFT_ORANGE, TFT_BLACK);
 tft.drawString(WiFi.softAPmacAddress(), 135, 220, GFXFF);
}

The drawDoorbellLog() method updates the LCD TFT screen with the date and time when the doorbell is pressed to keep a log of the exact date and time that the doorbell was pressed. It will show the most recent log at the top, and up to a total of 5 logs.

void drawDoorbellLog() {
  char strBuffer[22];
  DateTime now = rtc.now();
  // format datetime to become hh:mm:ss (dd/mm/yyyy)
  sprintf(strBuffer, "%02i:%02i:%02i (%02i/%02i/%04i)", now.hour(), now.minute(), now.second(), now.day(), now.month(), now.year());

   // shift array 1 position down
  memcpy(logArray, &logArray[1], sizeof(logArray) - sizeof(char) * 22);
  // update array with latest value
  memcpy(logArray[4], strBuffer, sizeof(char) * 22);

  tft.fillRect(1, 41, 318, 158, TFT_BLACK);
  tft.setFreeFont(FF17);
  // draw data in array to screen
  for (int i = 4; i >= 0; i--) {
      if (strcmp(logArray[i], "") != 0) {
         tft.setTextColor(TFT_WHITE, TFT_BLACK);
         tft.drawNumber(5 - i, 5, ((4 - i) * 30) + 50, GFXFF);
         tft.drawString(". Doorbell @", 17, ((4 - i) * 30) + 50, GFXFF);
         tft.setTextColor(TFT_RED, TFT_BLACK);
         tft.drawString(logArray[i], 125, ((4 - i) * 30) + 50, GFXFF);
      }
  }
}

The alarm() method sends an instruction to the Rainbow LED strip to blink and to sound the buzzer.

void alarm() {
  for (int i = 0; i < 4; i++) {
     digitalWrite(buzzerPin, LOW);
     strip.fill(strip.Color(255, 0, 0, strip.gamma8(255)));
     strip.show(); // Send the updated pixel colors to the hardware.
     delay(100);
     digitalWrite(buzzerPin, HIGH);
     strip.fill(strip.Color(0, 0, 0, strip.gamma8(255)));
     strip.show(); // Send the updated pixel colors to the hardware.
     delay(100);
  }
}

The InitESPNow() method will initialize ESPNow's wireless feature.

// Init ESP Now with fallback
void InitESPNow() {
  WiFi.disconnect();
  if (esp_now_init() == ESP_OK) {
     Serial.println("ESPNow Init Success");
  }
  else {
     Serial.println("ESPNow Init Failed");
     ESP.restart();
  }
}

Whenever new data is received, the OnDataRecv() method redraws the latest log onto the LCD screen and blinks the Neopixels, and sounds the buzzer to alert the user.

// callback when data is recv from Master
void OnDataRecv(const uint8_t *mac_addr, const uint8_t *data, int data_len) {
  Serial.print("Last Packet Recv Data: "); Serial.println(*data);
  drawDoorbellLog();
  alarm();
}

Edit the setup() method to initialize our pin and to call InitESPNow() to initialize ESP-Now on the ESP32. 

// initialize pins
pinMode(buzzerPin, OUTPUT);
digitalWrite(buzzerPin, HIGH);

// Set device as a Wi-Fi Station
WiFi.mode(WIFI_STA);

// This is the mac address of the Slave in AP Mode
Serial.print("AP MAC: "); Serial.println(WiFi.softAPmacAddress());

// Init ESPNow with a fallback logic
InitESPNow();

// Once ESPNow is successfully Init, we will register for recv CB to
// get recv packer info.
esp_now_register_recv_cb(OnDataRecv);

Initialize the Clock Module and set the date and time.

// initialize clock module
if (! rtc.begin()) {
  Serial.println("Couldn't find RTC");
  Serial.flush();
  while (1) delay(10);
}

if (rtc.lostPower()) {
  Serial.println("RTC lost power, let's set the time!");


  // When time needs to be set on a new device, or after a power loss, the
  // following line sets the RTC to the date & time this sketch was compiled
  rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));


  // This line sets the RTC with an explicit date & time, for example to set
  // January 21, 2014 at 3am you would call:
  // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
}

Finally, initialize our TFT and rotate it to landscape mode, then draw the start screen. Initialize our Neopixel and set its brightness.

// initialize tft
tft.init();
tft.setRotation(3);
tft.fillScreen(TFT_BLACK);
drawStartScreen();

// initialize neopixel
strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip.show(); // Turn OFF all pixels ASAP
strip.setBrightness(BRIGHTNESS);

Upload the programs to both the Doorbell Transmitter and Receiver. Now press the button on the Doorbell Transmitter.

Watch the data log appear on the TFT screen! Now you know if you missed any package from the courier while you are away!

Resources