In this project we’ll modify a commercial motion sensor (powered with mains voltage) with an ESP8266 to log data whenever motion is detected. The data will be sent to Node-RED using MQTT communication protocol. The ESP8266 will be powered through the motion sensor phase out wire using the HLK-PM03 AC/DC converter.
Prerequisites
Before continuing with this project, we recommend taking a look at the following resources:
- Getting Started with ESP8266 WiFi Transceiver (Review)
- Power ESP8266 with Mains Voltage using Hi-Link HLK-PM03
- What is MQTT and How It Works
- You need to have installed in your Raspberry Pi: Node-RED, Node-RED Dashboard, and Mosquitto MQTT broker
- ESP8266 GPIOs and Pinout Reference
Parts Required:
Here’s a list of the parts needed for this project:
- PIR Motion Sensor 220V (or 110V PIR Motion Sensor)
- ESP8266-01 – read Best ESP8266 Wi-Fi Development Boards
- ESP8266-01 Serial Adapter (to upload code to the ESP8266)
- Hi-Link HLK-PM03 (to convert mains voltage to DC 3.3V)
- Small protoboard
- Fuse Slow Blow (200mA)
- 47 uF electrolytic capacitor
- Raspberry Pi (to host Node-RED and MQTT broker) – read Best Raspberry Pi Kits
You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!
Project Overview
This project is divided into three parts:
- Building the circuit
- Writing and uploading the ESP8266 code
- Creating the Node-RED flow
The following image shows a high-level overview of the project we’ll build.
Motion Sensor
We’ll hack a commercial motion sensor that has enough space to put an ESP-01 and the HLK-PM03 AC/DC converter module inside. We bought our motion sensor for $5 in a local store.
When the PIR sensor detects motion, there’s power coming out from the red hot wire that can turn on a lamp or a device. Your motion sensor should have a similar wiring diagram on the lid or printed in the instructions manual.
In our project, the motion sensor output load is the HLK-PM03 AC/DC converter module that will power the ESP8266.
Related: Power ESP8266 with AC voltage using HLK-PM03 Converter
The HLK-PM03 module AC/DC converter supplies 3.3V from either 110VAC or 220VAC. This makes it perfect to power the ESP8266 from mains voltage.
In summary, when motion is detected there is power reaching the ESP8266. The ESP8266 can execute tasks for as long as the motion sensor is triggered.
You may need to adjust the time duration that the sensor stays on, so your ESP8266 has enough time to execute its tasks. The sensor should have a knob to adjust the time (and another to adjust luminosity).
In our example, whenever the ESP8266 powers up, it runs a sketch that sends information to Node-RED via MQTT to log the date and time that motion was detected.
Instead of sending information to Node-RED, you can execute other tasks, such as:
- Log data to a Google Spreadsheet;
- Send an email warning that motion was detected;
- Send notifications to your smartphone.
These tasks can be easily done using IFTTT.
1. Building the Circuit
The following schematic diagram shows the circuit for this project.
Remove your PIR motion sensor lid. Inside, it should have three wires: phase in, neutral, and phase out. Follow these steps:
- Wire phase in (brown) and neutral (blue) to the motion sensor
- Wire neutral (blue) and phase out (red) to the HLK-PM03 input
It’s recommended to add a slow blow fuse right before the HKL-PM03 converter and a capacitor to the output.
Note: if you’re using an ESP8266 that is always powered on with the HLK-PM03, we recommend using this protection circuit.
The HLK-PM03 outputs 3.3V and GND. These are connected to the ESP8266 VCC and GND pin to power it.
We’ve built the HLK-PM03 and ESP8266 circuit on a small protoboard to save space. We’ve also added some header pins to place the ESP8266-01. This way you can plug and unplug the board every time you need to upload new code.
Recommended reading: ESP8266 GPIOs and Pinout refence guide
2. Writing and Uploading the ESP8266 Code
We’ll program the ESP8266 using the Arduino IDE. In order to upload code to your ESP8266, you need to install the ESP8266 add-on first, if you haven’t already (Install the ESP8266 Board in Arduino IDE).
You’ll also need to install the PubSubClient library to create an MQTT client with the ESP8266. The PubSubClient library provides a client for doing simple publish/subscribe messaging with a server that supports MQTT (basically allows your ESP8266 to talk with Node-RED).
- Click here to download the PubSubClient library. You should have a .zip folder in your Downloads folder
- Unzip the .zip folder and you should get pubsubclient-master folder
- Rename your folder from
pubsubclient-masterto pubsubclient - Move the pubsubclient folder to your Arduino IDE installation libraries folder
Then, copy the following code to your Arduino IDE, but don’t upload it yet. You need to make a few modifications to make it work for you.
You need to edit the code with your own SSID, password and MQTT Broker IP Address (Raspberry Pi IP Address).
/*********
Rui Santos
Complete project details at https://randomnerdtutorials.com
*********/
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
// Replace with your SSID, password and MQTT broker IP address
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
const char* mqtt_server = "REPLACE_WITH_YOUR_MQTT_BROKER_IP";
//For example
//const char* mqtt_server = "192.168.1.144";
WiFiClient espClient;
PubSubClient client(espClient);
void setup_wifi() {
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
randomSeed(micros());
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Create a random client ID
String clientId = "ESP8266Client-";
clientId += String(random(0xffff), HEX);
// Attempt to connect
if (client.connect(clientId.c_str())) {
Serial.println("connected");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void setup() {
pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output
digitalWrite(BUILTIN_LED, HIGH);
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
while (!client.connected()) {
reconnect();
}
Serial.print("Motion Detected");
// Publish MQTT message
String mqttMessage = "Motion Detected";
client.publish("esp/pir", mqttMessage.c_str());
}
void loop() {
client.loop();
}
Include your network credentials
You need to include your network credentials in the following lines.
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
Include your MQTT broker IP address
You also need to include your MQTT broker IP address.
const char* mqtt_server = "REPLACE_WITH_YOUR_MQTT_BROKER_IP";
To find your MQTT broker IP address, it should be configured first. We’re using Mosquitto broker hosted on a Raspberry Pi. Follow the next resources if you haven’t done that yet:
How the code works
The code is very simple. It simply publishes a message on a topic. For testing purposes, we’re also turning the on-board LED on every time the ESP8266 is powered. This is done on the setup() because it will only be executed once (when motion is detected and while the ESP8266 is powered).
void setup() {
pinMode(BUILTIN_LED, OUTPUT); // BUILTIN_LED pin as an output
digitalWrite(BUILTIN_LED, HIGH);
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
while (!client.connected()) {
reconnect();
}
Serial.print("Motion Detected");
// Publish MQTT message
String mqttMessage = "Motion Detected";
client.publish("esp/pir", mqttMessage.c_str());
}
Uploading the code
After modifying the code with your network credentials, you can upload it to your board. To upload the code to the ESP8266-01 you need a serial adapter or an FTDI programmer.
3. Creating the Node-RED Flow
Before creating the flow, you need to have installed in your Raspberry Pi:
Importing the Node-RED flow
To import the provided Node-RED flow, go to the GitHub repository or click the figure below to see the raw file, and copy the code provided.
Next, in the Node-RED window, at the top right corner, select the menu, and go to Import > Clipboard.
Then, paste the code provided and click Import.
Here’s the imported flow. It receives the ESP8266 MQTT messages and logs the time whenever motion is detected.
We’ve also added two buttons to clear and refresh the log.
Node-RED Dashboard
After making all the necessary changes, click the Deploy button to save all the changes.
Now, your Node-RED application is ready. To access Node-RED Dashboard and see how your application looks, access any browser in your local network and type:
http://Your_RPi_IP_address:1880/ui
Demonstration
Now, you can test your project. Whenever motion is detected, the ESP8266 powers up and sends a message via MQTT to Node-RED.
If you go to your Node-RED Dashboard you can see all the logs of when motion was detected. Your application should look as in the following figure.
The dashboard shows the last time motion was detected and all previous logs. You also have the option to refresh the log, and clear the log. Please note that clearing the log is irreversible.
Wrapping Up
This project showed how you can hack a commercial motion sensor with an ESP8266 to make it smarter. You can easily power the ESP8266 with the HLK-PM03 from mains voltage and make a compact circuit that can fit in the motion sensor.
Whenever motion is detected the ESP8266 is powered and executes a task. In this case, it publishes an MQTT message to log the time motion was detected, but you can easily write code to execute any other task.
We hope you’ve found this project useful. If you liked this project, you may also be interested in one the following resources:
i have a MQTT password on my server, where do i put my password in your sketch please
Hi Nigel.
Take a look at this PubSubClient library example: github.com/knolleary/pubsubclient/blob/f029640ee6365c58ebfe66ebf6bf1733d322f2ec/examples/mqtt_auth/mqtt_auth.ino
It connects to an MQTT server, providing username and password.
Regards,
Sara
Very helpful
Thanks
It appears that the node-red flow cannot be imported? If I go to github it goes immidiately to raw and when I copy this raw code to clipboard and try to import from there it is not recognized as a correct flow? pls advice 😉
Hi.
What is the exact error that you’re getting?
the flow is not recognized to be valid when I try to import it. (The red install button does not show up when I paste the flow from the clipboard.
sorry, the red import button does not show up.
Hi again.
I’ve tested and everything is working fine.
You probably have not copied the code properly. If there is something missing, Node-RED doesn’t let you import.
You’re probably missing the first or the last character of the code. Make sure you copy everything from here: https://raw.githubusercontent.com/RuiSantosdotme/Random-Nerd-Tutorials/master/Projects/ESP8266-HLK/NodeRED_Motion_Datalogger.txt
Can you try again?
Regards,
Sara
something wrong with my github on win10? ……. Tried the same on my Linuxbox and it works like a charm 😉 thanks for your support /Henk
I’m glad it’s working now 😀
Regards,
Sara
Excellent Solution. Hadn’t thought of this. Now used this with some timer code to create motion lights that are not wired to the sensor.
Used Peter Scargils Big timer node.
Thanks to the Santos’s.
You’re welcome! I’m glad it was helpful
Hello Sara or Rui,
I downloaded the sketch from the url above, but when I try to compile the sketch. But then I get some errors:
sketch_feb09a:63:11: error: ‘BUILTIN_LED’ was not declared in this scope
pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output
exit status 1
‘BUILTIN_LED’ was not declared in this scope
Hi.
Just replace BUILTIN_LED with 2.
pinMode(2, OUTPUT);
Regards,
Sara
Hi, how fast does the motion sensor react?
I mean, powering up the ESP8266 already takes some time.
Regards,
Stefan
Hi Stefan.
In our experiments it was almost instantaneous. But I think it depends on the motion sensor you use.
We didn’t notice much delay in the response.
Regards,
Sara
Hi Sara,
Thanks for your comment!
This sounds very promising.
And thanks a lot for this great article!
regards,
Stefan
This is a great step by step description, as many others on this site. I think the circuit is somewhat overcomplicated with double ac to dc convertion: first on PIR itself and second is by HLK-PM03 unit. Is it not easier to connect to PIR’s low voltage circuit (and convert it if needed to 3.3V)? Definitely safer as you wouldnt be connecting mains voltage to your prototype pcb in such case (with 5mm clearance between neutral and load!)
Thank you for sharing this interesting custom solution. How long does it take from the moment the PIR sensor is turned on to the indication on the MQTT server? How long does the ESP8266 boot up?
Hi.
I didn’t measure the time but if I remember correctly, it was almost instantly.
Regards,
Sara
Question – just getting started with messing around with some builds so very new to this. Is it possible to have the board tied to the line voltage so it is always powered and then just have it sense when the red is triggered? Any advantages to it always being online? I’m thinking something along the lines of Home Assistant integration.
Thanks!
Edit – just to be clear I mean have the tap the line voltage with the HLK-PM03, not deliver line voltage to the board. Then I would imagine another HLK tapped off the red. That way you could still allow the lights to function as normal but make them “smart”.
HI,
I had the problem of entering MQTT user and password and I made these changes:
I included in the declarations
const char* MQTT_username = “your user_name mqtt”;
const char* MQTT_password = “you pw mqtt”;
in the reconnect function this change:
if (client.connect(“NodeMCUClient”, MQTT_username, MQTT_password)) {
instead of:
if (client.connect(clientId.c_str())) {
and the mandatory loop function:
void loop() {
}
Once these changes have been made everything works perfectly.
Thank you