Wio Terminal: Arduino Smart Doorbell (with Code!)
In this step by step tutorial, learn how to create your very own Arduino Smart Doorbell with your Wio Terminal! This project builds a doorbell that automatically detects the presence of movement, then notifies you on your mobile phone wherever you are. You can also follow along with any Arduino-compatible development board.
Introduction to the Smart Doorbell Project
There are many reasons why you might want a smart doorbell over the conventional one. For example, you might have a baby in your home and you don’t want your dog to bark and wake her up every time a visitor comes by. Or, you might simply want to know when there are visitors when you aren’t home!
Whatever the case, we want our Arduino smart doorbell to be able to do two key things:
- Automatically detect human presence
- Discreetly notify the user of that presence
To achieve this, we will make use of passive infrared sensors to detect motion. For the IoT side of things, there are many platforms that we can use, such as Microsoft Azure IoT and Google Cloud Platform. After some experimentation, however, I’ve decided to use Blynk for this project since it is easy to set up and also provides a readily customisable mobile application interface!
Passive Infrared (PIR) Motion Sensors
Since PIR sensors are at the core of this project, it’s worth taking some time to understand how they work.
Infrared Light
First of all, infrared light is simply a type of light that is invisible to the human eye. IR is all around us, emitted by all living things that give off heat. They are also used in many electronic applications, such as sending communication signals and thermal imaging.
PIR Sensor: Principle of Operation
A PIR sensor consists of two slots that each contain IR sensitive material.
When an infrared-emitting body such as a human passes by, it will first intercept one of the IR slots, which causes a positive differential relative to the other IR slot. When the human then leaves the detecting area, the opposite occurs and a negative differential is created. The resulting signal is then detected collectively to produce a “HIGH” output.
It’s important to understand that it is change that generates a positive output in our PIR sensor. If an infrared-emitting object remained still in the detecting area, our PIR sensor would eventually output “LOW”!
Typically, PIR sensors are covered by a lens known as a Fresnel lens. This lens focuses IR waves from the surroundings onto the sensor. Now, instead of a detecting area of two blocks, we can cover a much wider area instead!
Bringing the Wio Terminal Online with Blynk
When our Wio Terminal detects motion via the PIR Sensor, we want to receive a notification for it remotely. To do this, we’ll use Blynk.
Blynk is an IoT platform that allows us to quickly build IoT applications regardless of what hardware we are using. It can also be used for device management, data analytics and machine learning, but today we’ll simply use it to receive messages from our Wio Terminal, and to remotely toggle some settings.
Although Blynk will cost enterprises who want to use it to implement IoT on a large scale, their Developer Plan is free for individual use to build personal projects which is sufficient for today’s purpose.
Platform Introduction: Wio Terminal
Today’s tutorial will be done with a Wio Terminal and the Grove Mini PIR Motion Sensor!
The Wio Terminal is a fully integrated microcontroller development platform that supports Arduino and MicroPython. It carries Bluetooth, Wi-Fi connectivity, and comes with an LCD screen, onboard IMU, microphone, buzzer, light sensor and infrared emitter. The Wio Terminal also features two multifunctional Grove ports which can be used to interface easily with plug-and-play Grove modules.
The Wio Terminal is a great one-stop solution for today’s project. Since we can simply make use of the inbuilt WiFi and plug our PIR sensor in via Grove, we’ve eliminated the need for soldering or dealing with messy wires. We can even combine it with its dedicated battery chassis to make our doorbell a mobile and conveniently wall-mountable solution!
Naturally, you can follow along with this tutorial with any other Arduino board and PIR Sensor, but you’ll have to work a little harder to get the wiring done!
You can pick up a Wio Terminal from the Seeed Online Store.
Note: While I’ve chosen the Grove Mini PIR Motion Sensor for its compact size and low cost, feel free to choose from the following Grove PIR Motion Sensor modules. Some of them will offer additional functionality such as distance measurement and adjustable holding times.
Now, let’s get into the tutorial proper!
Install Libraries
First of all, if this is your first time working with your Wio Terminal, I highly recommend you visit this page to install the required basic libraries and get started.
Next, follow the instructions at this link to set up the WiFi capabilities on your Wio Terminal. This will involve updating the firmware of your Wio Terminal and installing several Arduino libraries.
Finally, we will have to install the Blynk Library. Download the library from this GitHub repository as a ZIP and install it as you have the previous libraries.
Hardware Setup
Plug your Grove PIR Sensor module into the Wio Terminal as shown below. That’s it!
Set Up Blynk
To start, follow the instructions on the Seeed Wiki to set up a connected dashboard for interfacing with your Wio Terminal. I’ve set up my dashboard as shown below with a value display, a styled button, and a notification.
If you want to receive email alerts instead of notifications, you can do that as well. Read the documentation to learn how to do so.
Smart Doorbell Arduino Code
Now, we have to program our Wio Terminal to receive data from our PIR sensor, as well as upload the collected data to the Blynk servers.
First, take a look at the code below. You will also need to copy the Free_Fonts.h file from:
~/Arduino/libraries/Seeed_LCD_master/examples/320 x 240/All_Free_Fonts_Demo
and attach this header file to your sketch location in order to compile the sketch.
Alternatively, download it together with the code from this link.
// Wio Terminal: Arduino Smart Doorbell
// Author: Jonathan Tan
// Feb 2021
// Written for Seeed Wio Terminal
#define BLYNK_PRINT Serial
#include <TFT_eSPI.h>
#include <rpcWiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleWioTerminal.h>
#include "Free_Fonts.h"
// Define PINs
#define PIR PIN_WIRE_SCL
#define LCD_BACKLIGHT (72Ul)
// Define Timeouts
#define LCDTimeout 20000
#define notifyTimeout 20000
TFT_eSPI tft;
BlynkTimer timer;
// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "YOURAUTHKEY";
// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "YOURWIFISSID";
char pass[] = "YOURWIFIPASSWORD";
// Standard Outputs
String person = "Movement Detected.";
String absent = "All clear.";
// Variables
int person_state;
int notify_me;
long LCDmillis;
long notifyMillis;
long millisMarkerDebug;
BLYNK_WRITE(V1) {
int pinValue = param.asInt();
// Debug: Check Toggle Inputs
Serial.print("Input Value: ");
Serial.println(pinValue);
// Toggle According to Pin Value
if (pinValue == 1) {
notify_me = 1;
} else {
notify_me = 0;
}
}
void sendPIRDetect() {
if(digitalRead(PIR)) {
person_state = 1;
Blynk.virtualWrite(V0, person);
if (notify_me == 1 && millis() - notifyMillis > notifyTimeout) {
Blynk.notify("Movement detected.");
notifyMillis = millis();
}
} else {
person_state = 0;
Blynk.virtualWrite(V0, absent);
}
}
void eraseLCD() {
tft.fillRect(0, 0, TFT_HEIGHT, TFT_WIDTH, TFT_BLACK);
}
void setup() {
// Debug console
Serial.begin(9600);
// Actual Setup
pinMode(PIR, INPUT);
tft.begin();
tft.setRotation(3);
tft.fillScreen(TFT_BLACK);
tft.println("Initialising...");
Blynk.begin(auth, ssid, pass);
timer.setInterval(5000L, sendPIRDetect);
tft.println("Connected!");
delay(2000);
tft.fillScreen(TFT_BLACK);
}
void loop() {
Blynk.run();
timer.run();
// Turn LCD off after
if ( person_state == 0 && (millis() - LCDmillis) > LCDTimeout) {
eraseLCD();
digitalWrite(LCD_BACKLIGHT, LOW);
}
if (person_state == 1 && (millis() - LCDmillis) > LCDTimeout) {
digitalWrite(LCD_BACKLIGHT, HIGH);
LCDmillis = millis();
tft.setTextDatum(MC_DATUM);
tft.setFreeFont(FSSB18);
tft.drawString("Hello!", 160, 100);
tft.setFreeFont(FSS9);
tft.drawString("A doorbell notification", 160, 135);
tft.drawString("will be sent.", 160, 155);
}
// For Debugging in Serial Terminal
if ( (millis() - millisMarkerDebug) > 1000) {
millisMarkerDebug = millis();
if (digitalRead(PIR)) {
Serial.println("Movement Detected.");
} else {
Serial.println("All Clear.");
}
}
}
Adjusting the Parameters
If you want to use the code directly, you must adjust the following fields to include your own project authentication key and WiFi settings.
// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "YOURAUTHKEY";
// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "YOURWIFISSID";
char pass[] = "YOURWIFIPASSWORD";
You can also alter the following parameters. LCDTimeout dictates the number of milliseconds before the LCD screen is turned off to save power, while notifyTimeout dictates the duration in milliseconds to wait before the notification is sent to your mobile phone.
// Define Timeouts
#define LCDTimeout 20000
#define notifyTimeout 20000
Brief on Programming with Blynk
In the setup function, we first initialise our PIR sensor input pin, the LCD display, as well as our connection to the Blynk server.
void setup() {
// Debug console
Serial.begin(9600);
// Actual Setup
pinMode(PIR, INPUT);
tft.begin();
tft.setRotation(3);
tft.fillScreen(TFT_BLACK);
tft.println("Initialising...");
Blynk.begin(auth, ssid, pass);
timer.setInterval(5000L, sendPIRDetect);
tft.println("Connected!");
delay(2000);
tft.fillScreen(TFT_BLACK);
}
You’ll notice that the timer.setInterval() function call is used to call our sendPIRDetect() function every 5 seconds.
timer.setInterval(5000L, sendPIRDetect);
The timer is Blynk’s proprietary syntax and is used instead of delay() to periodically perform tasks. This is because delay() function calls will pause the program entirely and cause our Wio Terminal to lose its connection with the Blynk servers.
Be careful when playing with the timer intervals. You might be tempted to set the intervals short for debugging, but this will actually cause your device to be disconnected from the Blynk servers automatically due to spam detection!
Sending Sensor Data
To receive data from our Wio Terminal, Blynk uses virtual pins that we can write to. We can later use these virtual pins to display the data on our dashboard in the Blynk app. In addition, we will also use Blynk.notify() to send a notification to our application in the event that movement is detected.
void sendPIRDetect() {
if(digitalRead(PIR)) {
person_state = 1;
Blynk.virtualWrite(V0, person);
if (notify_me == 1 && millis() - notifyMillis > notifyTimeout) {
Blynk.notify("Movement detected.");
notifyMillis = millis();
}
} else {
person_state = 0;
Blynk.virtualWrite(V0, absent);
}
}
Here, we also use the person_state variable to record the presence of movement so that other parts of our program can respond accordingly. We will use this variable to control our LCD greeting screen later.
Admittedly, I found while testing the code that these notifications can get annoying pretty quickly if we don’t put some kind of limit on them. Hence, I added a timeout condition where the notification is only sent once per 20 seconds by default. I also added a toggle on the dashboard that would allow us to set if we want notifications to be delivered.
To implement this toggle, we have to use virtual pins again.
BLYNK_WRITE(V1) {
int pinValue = param.asInt();
// Debug: Check Toggle Inputs
Serial.print("Input Value: ");
Serial.println(pinValue);
// Toggle According to Pin Value
if (pinValue == 1) {
notify_me = 1;
} else {
notify_me = 0;
}
}
This simple bit of code is a callback that is run every time we toggle the button on our mobile phone. In this case, we simply switch the notify_me state variable between 0 and 1 according to the pin’s value. This variable is then used in the sendPIRDetect() function to determine whether or not the notification should be sent.
Greeting our Guests with the LCD Display
Since we have an LCD display built into our Wio Terminal, why not build a simple screen to let our guests know we’re aware of their arrival?
if ( person_state == 0 && (millis() - LCDmillis) > LCDTimeout) {
eraseLCD();
digitalWrite(LCD_BACKLIGHT, LOW);
}
if (person_state == 1 && (millis() - LCDmillis) > LCDTimeout) {
digitalWrite(LCD_BACKLIGHT, HIGH);
LCDmillis = millis();
tft.setTextDatum(MC_DATUM);
tft.setFreeFont(FSSB18);
tft.drawString("Hello!", 160, 100);
tft.setFreeFont(FSS9);
tft.drawString("A doorbell notification", 160, 135);
tft.drawString("will be sent.", 160, 155);
}
Here, I’ve added two additional conditional statements to our main loop. These statements continuously check for the person_state variable that we previously toggled, but are only run every 20 seconds to avoid erratic screen flickering.
If the PIR sensor reads HIGH, we will display a message to let our guests know that we’re being notified of their arrival. Otherwise, we will turn off our LCD backlight to save power with the following function call.
digitalWrite(LCD_BACKLIGHT, LOW);
Note that the LCDmillis variable is only ‘reset’ with the on-state code block. This allows us the LCD to be turned on again immediately by any subsequent movement!
Troubleshooting the Code
When it comes to IoT applications WiFi networks, microcontrollers can sometimes take awhile. If you’re getting stuck, trying one of the following may help.
1. Check WiFi Connectivity
Ensure that your WiFi SSID and Password are correct by uploading the following code and checking for connectivity.
#include "rpcWiFi.h"
const char* ssid = "yourNetworkName";
const char* password = "yourNetworkPassword";
void setup() {
Serial.begin(115200);
while(!Serial); // Wait for Serial to be ready
// Set WiFi to station mode and disconnect from an AP if it was previously connected
WiFi.mode(WIFI_STA);
WiFi.disconnect();
Serial.println("Connecting to WiFi..");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.println("Connecting to WiFi..");
WiFi.begin(ssid, password);
}
Serial.println("Connected to the WiFi network");
Serial.print("IP Address: ");
Serial.println (WiFi.localIP()); // prints out the device's IP address
}
void loop() {
}
2. Reset your Wio Terminal
You can do this by pulling downwards on the power button, then releasing it. Or, reupload your code in the Arduino IDE.
3. Wait…. And Try Again
If all else fails, simply wait and try again later. Sometimes, connections to external servers can take awhile to be established.
The Final Product
Here we have the final product! An automatic doorbell that detects someone’s presence with their movement, remote messages sent to our phone so we know when we have guests, as well as an LCD display to greet them!
Here we see the PIR sensor and LCD screen’s greeting in action!
Meanwhile, the dashboard allows us to receive notifications and monitor the sensor.
Summary
And that concludes today’s tutorial. I hope you were able to follow along and build your very own Arduino smart doorbell with your Wio Terminal! You can easily tweak the existing code to give you alerts under a variety of circumstances, or even to send various hardware commands to your Wio Terminal remotely. In addition, you’ll find that there are a great variety of IoT applications that can be created with a MCU like the Wio Terminal, depending on what sensors we attach to them.
If you’re keen to try other projects for the Wio Terminal, please visit the following links:
- Wio Terminal: Arduino Customisable Timer (with Code!)
- Learn TinyML using Wio Terminal and Arduino IDE #1 Intro
- Resource Roundup for Wio Terminal: Tutorials, Reviews, and Projects from Community