I already have one motion sensor in the house but wanted a cheaper option to add to a few more rooms. Philips want £30 per sensor and I can make this for around £5 and a 3 week wait from China. Sure it hasn't got an ambient light sensor like the philips but that isn't needed for me, I could always add one later anyway for under £2.
It consists of a Wemos D1 mini and a HC-SR501 PIR sensor.
Instructions:
Wire up your PIR as follows:
Wemos D1 --> PIR signal (middle wire)
Wemos 5v --> PIR 5v (left wire when connector is facing you)
Wemos GND --> PIR GND (Right wire)
Set up the bridge to allow the connection. Follow this guide to set up your unique user ID.
https://developers.meethue.com/docum...etting-started
Set up the arduino:
First you'll need to add the ESP8266 library to your Arduino install.
Go to Sketch--->Include Library--->Manage Libraries
Install the ESP8266Wifi by Ivan Grokhotkov library.
Copy and paste the following. Don't use the code on the guys github as it's slightly wrong (it's missing the HTTP/1.1 string on the end of the PUT command), this one is tested working.
Code:
/*
esp8266Huemotion
https://github.com/LeskoIam/esp8266Huemotion
polensek.matevz@gmail.com
Used instead of very expensive Hue motion sensor.
It's basicly a D1 mini, which breaks out pins from esp8266 WiFi module and adds some
periphery to enable USB serial communication.
Operation:
It uses PIR sensor attached to pin D1 (arduino library maps this pin to pin 5) to
detect motion and turn on specified Hue light bulb if all conditions are met. These
conditions are:
- It's night time. This is determined based on if specified light bulb is on or off.
State of the light bulb is checked and updated every set number of minutes
("lights_check_delay").
- If motion is detected. Specified light bulb is turned on for set amount of time
("motion_detected_delay"). Motion is still detected and updated when light is turned on,
this means light is turned off only after no motion is detected for set delay.
Deep sleep:
If enabled, you can put module to sleep while "lights_check_delay" is active. It can conserve
a lot of power and is specialy useful when module is powered with batteries. This is controlled
with "use_deep_sleep" variable.
Minor hardware modification is also necessary. Connect D0 pin to RST pin. This connection is
used to wake up the module after sleep period is over.
The circuit:
- Philips Hue lights
- D1 mini or clone (esp8266).
- PIR motion sensor (HC-SR501).
Settings:
Look for code in #### block.
Mandatory settings:
- use_deep_sleep
- ssid
- password
- bridge_ip, port
- user
- light
Optional:
- motion_detected_delay
- lights_check_delay
- hue_on
- hue_off
*/
#include <ESP8266WiFi.h>
//// Global settings and variables
// ################################################
// Deep Sleep
bool use_deep_sleep = 0; // If deep sleep is enable don't forget to connect D0 to RST pin
// Wifi Settings
const char* ssid = "YOURSSID";
const char* password = "YOURPASSWORD";
// Hue settings
const char* bridge_ip = "YOURBRIDGEIP"; // Hue bridge IP
const int port = 80;
String user = "YOURUSERNAME";
String light = "YOURLIGHTNUMBER"; // Number of the light you want to control
// Motion timing
unsigned long motion_detected_time = 0; // When was motion last detected
unsigned long motion_detected_delay = 60*1000; // Keep light on for that many seconds after last motion was detected
// All lights off check timing
unsigned long lights_check_time = 0; // When was light state last checked
unsigned long lights_check_delay = 1*60*1000; // Check light state every that many minutes
// Commands
String hue_on = "{\"on\":true, \"bri\":5, \"xy\":[0.1540,0.0806]}";
String hue_off = "{\"on\":false}";
// ################################################
// Pin settings
// PIR sensor is attached to D1 mini D1 pin which maps to pin 5 for arduino library
int pirPin = 5;
int light_state = 0; // Internally track state of light
int first_loop = 1; // Is this first loop
int check_lights_first_loop = 1; // Same as above but for checking light state
bool night_time; // master logic variable
void setup()
{
Serial.begin(115200);
delay(10);
// Connect to WiFi network
Serial.println();
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println();
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
Serial.println();
Serial.print("MAC: ");
Serial.println(WiFi.macAddress());
// Set PIR pin as input pin
pinMode(pirPin, INPUT);
if (use_deep_sleep)
{
// Connect D0 to RST to wake up
pinMode(16, WAKEUP_PULLUP);
}
Serial.println("Waiting 2 seconds for stable sensor readings...");
delay(1000);
Serial.println("Setup done. Main loop starting in a second...");
delay(1000);
Serial.println();
}
void loop()
{
//// Check state of the light and set nigh_time accordingly
if ((((lights_check_time + lights_check_delay) < millis()) || check_lights_first_loop == 1) && (light_state != 1))
{
Serial.print("Checking state of light ");
Serial.println(light);
check_lights_first_loop = 0;
if (is_light_on() == 1) { night_time = 0; }
else { night_time = 1; }
lights_check_time = millis();
if (use_deep_sleep)
{
if (!night_time)
{
// Go to deep sleep. Don't forget to convert milliseconds to microseconds
Serial.printf("\nDEEP SLEEP for %i microseconds\n\n", lights_check_delay*1000);
ESP.deepSleep(lights_check_delay * 1000);
}
}
}
// Some debug prints
Serial.println("-----");
Serial.print("night_time: ");
Serial.println(night_time);
Serial.print("motion_detected_time: ");
Serial.println(motion_detected_time);
Serial.print("light_state: ");
Serial.println(light_state);
//// Main motion-light logic
// Enter only if it is nigh time and ligh is not on because of us
if ((night_time == 1) || (light_state == 1))
{
// Read PIR sensor
int motion = digitalRead(pirPin);
Serial.print("motion: ");
Serial.println(motion);
// If motion is detected
if (motion == HIGH)
{
// And light is off because of us.
// This also prevents multiple turn ons (sensor output stays on for around 2 seconds)
if (light_state == 0)
{
// Turn light on only if previous on time delay has passed or if this is first loop
// first_loop check was added to handle situation when motion-light has
// not yet been running for more than motion delay time
if (((motion_detected_time + motion_detected_delay) < millis()) || first_loop == 1)
{
Serial.println("Turning light on");
light_control(hue_on);
light_state = 1;
first_loop = 0;
}
}
// Detect every motion and update detection time
motion_detected_time = millis();
}
else
{
// Only turn off light if they were turned on by us
if (light_state == 1)
{
// Turn light off only if on time delay has passed
if ((motion_detected_time + motion_detected_delay) < millis())
{
Serial.print("No motion for ");
Serial.print(motion_detected_delay/1000);
Serial.println(" seconds. Turning light off");
light_control(hue_off);
light_state = 0;
}
}
}
}
delay(333);
}
/* light_control.
* Send PUT command to hue light (bridge). Function takes json formated command.
* Returns:
* 1 if operation successful
* 0 if operation not successful
* -1 if error occurred
*/
bool light_control(String command)
{
int retval = 0; // return value
WiFiClient client; // WiFiClient class to create TCP connections
if (!client.connect(bridge_ip, port))
{
Serial.println("ERR>> light_control - Connection failed");
return -1;
}
// This will send PUT request to the server
client.println("PUT /api/" + user + "/lights/" + light + "/state HTTP/1.1");
client.println("Host: " + String(bridge_ip) + ":" + String(port));
client.println("User-Agent: ESP8266/1.0");
client.println("Connection: keep-alive");
client.println("Content-type: text/xml; charset=\"utf-8\"");
client.print("Content-Length: ");
client.println(command.length()); // PUT COMMAND HERE
client.println();
client.println(command); // PUT COMMAND HERE
// Wait 10 seconds for server to respond
unsigned long timeout = millis();
while (client.available() == 0)
{
if (millis() - timeout > 10000)
{
Serial.println("ERR>> light_control - Client timeout");
client.stop();
return -1;
}
}
// Read all the lines of the reply from server
while(client.available())
{
String line = client.readStringUntil('\r');
// Print line to serial if it's request status or json formated string
if (((line.indexOf("{") != -1) && (line.indexOf("}") != -1)) || (line.indexOf("HTTP") != -1)){ Serial.print(line); }
// If success string is found in reply we have successfully sexecuted command
if (line.indexOf("\"success\":") != -1){ retval = 1; }
}
Serial.println();
client.stop();
return retval;
}
/* are_all_lights_on
* Returns:
* 1 if operation successful
* 0 if operation not successful
* -1 if error occurred
*/
bool are_all_lights_on()
{
int retval = 0; // return value
WiFiClient client; // WiFiClient class to create TCP connections
if (!client.connect(bridge_ip, port))
{
Serial.println("ERR>> are_all_lights_on - Connection failed");
return -1;
}
// This will send GET request to the server
client.println("GET /api/" + user + "/lights HTTP/1.1");
client.println("Host: " + String(bridge_ip) + ":" + String(port));
client.println("Connection: close");
// Wait maximum of 10 seconds for server to respond
unsigned long timeout = millis();
while (client.available() == 0)
{
if (millis() - timeout > 10000)
{
Serial.println("ERR>> are_all_lights_on - Client timeout");
client.stop();
return -1;
}
}
// Read all the lines of the reply from server
while(client.available())
{
String line = client.readStringUntil('\r');
// Print line to serial if it's request status or json formated string
if (((line.indexOf("{") != -1) && (line.indexOf("}") != -1)) || (line.indexOf("HTTP") != -1)){ Serial.print(line); }
// If any light is off - all lights are not on
if (line.indexOf("\"on\":false") == -1){ retval = 1; }
}
Serial.println();
client.stop();
return retval;
}
/* is_light_on.
* Returns:
* 1 if operation successful
* 0 if operation not successful
* -1 if error occurred
*/
bool is_light_on()
{
int retval = 0;
// Use WiFiClient class to create TCP connections
WiFiClient client;
if (!client.connect(bridge_ip, port))
{
Serial.println("ERR>> is_light_on - Connection failed");
return -1;
}
// This will send GET request to the server
client.println("GET /api/" + user + "/lights/" + light);
client.println("Host: " + String(bridge_ip) + ":" + String(port));
client.println("Connection: close");
// Wait maximum of 10 seconds for server to respond
unsigned long timeout = millis();
while (client.available() == 0)
{
if (millis() - timeout > 10000)
{
Serial.println("ERR>> is_light_on - Client timeout");
client.stop();
return -1;
}
}
// Read all the lines of the reply from server and print them to Serial
while(client.available())
{
String line = client.readStringUntil('\r');
// Print line to serial if it's request status or json formated string
if (((line.indexOf("{") != -1) && (line.indexOf("}") != -1)) || (line.indexOf("HTTP") != -1)){ Serial.print(line); }
// Check if light is on
if (line.indexOf("\"on\":true") != -1){ retval = 1; }
}
Serial.println();
client.stop();
return retval;
}
Change the global variables for your SSID, password, bridge IP address, user ID and light number you want to control. You can also change how long you want it to stay on for. The Wemos will also go into deep sleep until new motion if the "use_deep_sleep" is set to 1. If you do this you will need to connect the D0 and RST pins of the Wemos.
Upload the code to your Wemos and that's it apart from printing your case.
https://www.thingiverse.com/thing:2955991
Social Networking Bookmarks