Inexpensive home automation for a lazy guy
One of the routines that I want to do in the morning is opening the curtains or blind of my home windows. It is not only great to see my living space is getting brighter, but also important for the plants to ready to receive sunlight all day long.
In reality, however, because I’m not really morning person, so I forget to do the simple task more than half of my weekdays.
There are always excellent solutions out there in terms of home automation as long as you are willing to pay the bill. Recently, IKEA has started to deliver promising blind solutions, which was long overdue (I believe, not in Canada at the moment of I’m writing the story). There are some affordable packages from other countries like China (But, their products will only work with their own mobile apps, and I don’t want to install all vendor’s app to my smartphone for many reasons). You can even hire a local contractor to replace your blinds with top-class home automation toolkits. (As you may already guess, it is not my options as well)
So, I think this simple task can be automated with Arduino with a motor, so I can not only forget one thing in my head in the morning but also save some money (overall cost is about $25 + my weekend time)
The outcome is here
Preparation
- An Arduino ESP8266 board (WeMos D1 Mini is recommended as it is the smallest)
- A stepper and the driver circuit board (if your blind is small 28BYJ-48 and ULN2003 would be ok, otherwise Nema 17 and A4988 are recommended as it delivers more torque, 10 times stronger than the other)
- A Joystick Module that can be used with Arduino
- Breadboard, soldering iron, board wires, and Arduino development environment on your computer
(The above links are only for references, you can get them in anywhere you’d like)
Circuit Setup
First, I think, it is a good idea to lay the parts out to understand the overall connection before you directly put everything to the board. Fritzing is mainly used for designing the circuits, but it is also a great tool for me to prepare the connections to clear off any confusion or finding missing parts. (If you are using Nema 17 motor, the following Fritzing diagram might be a little different from yours, because the motor has 4 wires for Bipolar connection)
- connect stepper to the socket of your driver board (in here as an example, ULN2003)
- connect INT1, 2, 3, 4 from ULN2003 to D5, D6, D7, D8 pins in Arduino ESP8266
- the joystick module is capable to operate multiple directions (horizontal and vertical), but the blind, we only need up and down, so choose only horizontal to connect between in the socket (HOZ) and A0 pin to Arduino
- Then, connect SEL (or SW) pin in the joystick to D4 in Arduino, which can be used for calibration later
- The rest of the connections are for power 5V (+: red) and (-:black)
Then, the real connection in the breadboard would be like the next photo.
Arduino Code
First, you will need to install the additional libraries from the Arduino environment. These libraries are fairly common, from Arduino, click on Tools -> Manage Libraries and search with the names and pick the latest versions.
- ESP8266
- Stepper
- NTPClient
Next, use the following Arduino code for small torque blind, code is in Github. There is a code portion to use your Wi-Fi connection, change them as yours.
#define WIFI_SSID “your WiFi SSID”
#define WIFI_PWD “your WiFi password”
#include <Stepper.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <NTPClient.h>
#include <WiFiUdp.h>// your WiFi settings
#define WIFI_SSID "your WiFi SSID"
#define WIFI_PWD "your WiFi password"#define BAUD_RATE 57600
#define DELAY_IN_LOOP 200
#define DELAY_IN_WIFI 500// Joystick Configuration
#define MAX_JOYSTICK 1024
#define PIN_JOYSTICK_X A0
#define PIN_JOYSTICK_SW 2 // D4 = GPIO_2
#define SW_OFF 1
#define SW_ON 0
#define JOYSTICK_THRESOLD 150
#define JOYSTICK_CENTER (MAX_JOYSTICK / 2)
#define JOYSTICK_RANGE_MIN 200
#define JOYSTICK_RANGE_MAX 900#define MOTOR_ON_MIN (JOYSTICK_CENTER - 400)
#define MOTOR_ON_MAX (JOYSTICK_CENTER + 400)
#define MOTOR_ONOFF_DELAY 100#define BLIND_OPEN_TIME_MIN (8 * 60 + 30) // open time 08:30
#define BLIND_OPEN_TIME_MAX (16 * 60 + 30) // close time 16:30
#define BLIND_CLOSE false
#define BLIND_OPEN true
#define BLIND_CYCLE 200 // it depends on blind types#define TIME_UPDATE_INTERVAL 0x0000FFFF
#define TIME_ZONE -5 // your timezone// Stepper Motor (28YBJ-48)
#define STEPS_PER_RESOLUTION 64
#define MOTOR_SPEED 220#define DEBUG_BEGIN(baudRate) Serial.begin(baudRate)
#define DEBUG_PRINT(_name, _value) Serial.print(_name); Serial.println(_value)unsigned long timeUpdate = TIME_UPDATE_INTERVAL;
bool motor_on = false;
bool blind_open = BLIND_CLOSE;WiFiUDP udp;
NTPClient ntp(udp, "pool.ntp.org", (TIME_ZONE * 60 * 60), TIME_UPDATE_INTERVAL * 60 * 1000);
Stepper stepper(STEPS_PER_RESOLUTION, D5, D7, D6, D8);void setup() {
DEBUG_BEGIN(BAUD_RATE);
connectWiFi();
stepper.setSpeed(MOTOR_SPEED);
motorOn(false);runBlind(BLIND_CLOSE);
ntp.update();
DEBUG_PRINT("time=", ntp.getFormattedTime());
}void loop() {
if (checkJoystick()) {
// do nothing
}
else if (checkTimeOfDay()) {
// do nothing
}
}bool checkJoystick() {
int x = analogRead(PIN_JOYSTICK_X);
int should_motor_on = ( MOTOR_ON_MIN > x || x > MOTOR_ON_MAX );
bool motorUpdated = (should_motor_on != motor_on);if (motorUpdated) {
motor_on = should_motor_on;
motorOn(motor_on);
}
if (motor_on) {
if (x < JOYSTICK_RANGE_MIN) {
stepper.step(1); // CW 1 step
}
else if (x > JOYSTICK_RANGE_MAX) {
stepper.step(-1); // CCW 1 step
}
} return motorUpdated;
}void motorOn(bool turnOn) {
int onoff = turnOn ? HIGH : LOW;
digitalWrite(D5, onoff);
digitalWrite(D6, onoff);
digitalWrite(D7, onoff);
digitalWrite(D8, onoff);
delay(MOTOR_ONOFF_DELAY);
}void runBlind(bool openBlind) {
DEBUG_PRINT("runBlind, open=", openBlind);
motorOn(true);
for (int i=0; i<BLIND_CYCLE; i++) {
stepper.step(openBlind ? 1 : -1); // CW : CCW
}
motorOn(false);
}bool checkTimeOfDay() {
bool blindUpdated = false;
if (timeUpdate <= 0) {
if (WiFi.status() == WL_CONNECTED) {
ntp.update();
}
DEBUG_PRINT("time=", ntp.getFormattedTime());
int hours = ntp.getHours();
int minutes = ntp.getMinutes();
int dailyTime = hours * 60 + minutes;
bool shouldBlindOpen = ( BLIND_OPEN_TIME_MIN < dailyTime && dailyTime < BLIND_OPEN_TIME_MAX );
blindUpdated = (shouldBlindOpen != blind_open);
if (blindUpdated) {
runBlind(shouldBlindOpen);
blind_open = shouldBlindOpen;
}
timeUpdate = TIME_UPDATE_INTERVAL;
}
timeUpdate--;
return blindUpdated;
}void connectWiFi() {
WiFi.mode(WIFI_STA);
WiFi.disconnect();
delay(DELAY_IN_LOOP); Serial.print("device mac: ");
Serial.println(WiFi.macAddress()); WiFi.begin(WIFI_SSID, WIFI_PWD);
Serial.print("Connecting to ");
Serial.print(WIFI_SSID);
Serial.println(" ...");
Serial.println("Waiting for WiFi");
int i = 0;
while (WiFi.status() != WL_CONNECTED) {
delay(DELAY_IN_WIFI);
Serial.print(".");
if (i >= 9) {
i = 0;
Serial.println("");
}
else {
i++;
}
}
Serial.println("connected"); Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
Serial.println("network is ready!\n");
}
Now, verify the code by click on the ‘verify’ icon in the most top left side of the Arduino editor.
If there is no error, then connect your ESP8266 board with the USB cable to your computer. You may need to install some serial port drivers depending on your OSes (Mac OS, Windows, Linux), then click on the second button to compile and deliver your code to the board.
3D Model of the pully
There is only one moving part in the project, the pully. If you do not have a 3D printer, check with your local libraries some of them provide free services for the hobbyist. I used the Autodesk Fusion 360 trial to create the models and convert them as STL file and GCode files (in Github), it really depends on the 3D printers.
Assemble and Installation
Soldering the parts in the perf board, and secure them with screws
Then cutting off the rest of the perf board, the put the 3D printed pully into the motor shaft. Done!
The actual installation also takes some time to verify the torque and the direction of the rotation or securing point to the window frame.
What is next
With this weekend project, I was able to automate a simple task in the morning (8:30 AM) and evening (4:30 PM) with cost about $25. The blind can be operated by joystick manually. It was great fun with a little spending. If you have any ideas or suggestions, please let me know.
I just ordered another stepper, which is 10 times stronger than the current stepper in order to use it with another larger window.
Next, I’m thinking to connect it with the home bridge in order to operate with Siri on the iPhone, just like what I did in my previous article. And, of course, I need an enclosure for the blind.