Remotely Operated Motorized Curtains
Team Members:
Jusa Martin
Roope Ruokonen
Sanyog Lamsal
Opening and closing curtains can sometimes be a hassle, especially if they are difficult to reach. This can be especially problematic for people who have mobility issues, such as the elderly or those with difficulties. Additionally, for those who have many curtains to operate, it can be a time-consuming and repetitive chore. In such cases, motorized curtains can provide a convenient solution to this.
Process of constructing Remotely Operated Motorized Curtains
The process of designing and assembling remotely operated motorized curtains consisted of determination of properties and restrictive elements of the system (section 1), choosing valid components for the system to run properly (section 2), coding (section 3), mechanical designing the system (section 4) and assembly of the system (section 5).
- Properties and restrictive elements are
Goal of the project is to build an automatic motorized roller curtain for a window
(dimensions W120xH145) that can be controlled remotely via phone with good
reliability. The control system should be as subtle as possible so that it would not be so
visible. The control system's size is limited by the adjacent wall restricting its length to
19 cm. Width and the depth of system is not limited. A power outlet is available at the
distance of ~2 meters measured from the axis of the curtains.
• Properties of the system include:
▪ Lifting and lowering the curtains / Step by step /
Full lowering/lifting
▪ Magnetic sensor for restricting the movement of the
curtains.
▪Wifi control of the curtains
• Scope: The scope of this project will be to open and close specifically
roller curtains or also known as roller blinds, and not for a wide range of
curtain styles or sizes.
- List of components
- Nema 17 Stepper motor - Works as a motor of the system
- DRV8825 stepper motor driver - Driver runs the motor in controlled manner
- Arduino RP2040 micro controller with Wifi and bluetooth - Microcontroller is the brains of the system
- Hall sensor: TLE4905L DIGITAL HALL SENSOR UNIPOLAR SWITCH - Works as a limit switch if motor has lost its current position
- Capasitor, 50V 47uF - Protects motor when starting the system
- 683-RSZ bearings,3x7x3mm, 6 pcs - Installed to both sides of the planets of the gearbox
- 606-2RS bearing, 6x17x6mm, 1 pc - Installed to cover of the gearbox for M6 screw
- 626 ZZ bearing, 6x19x6mm, 1 pc - Installed in the bearing housing in curtains' holder
- M3 threaded rod, M3x50, 4 pcs - Connects motor, gearbox and front cover of the motor housing
- M3 Cap head screw, M3x16, 3 pcs - Connects carrier and planets together with 683-RSZ bearings
- M3 nut, 7 pcs - Installed to threaded rods and M3 cap head screws
- M6 bolt, M6x50, 1 pc - Connects Motor/gearbox to curtain adapter
- PCB-board, 70x50mm, 1 pc - All the electronics are soldered to PCB-board
- 12V power supply with 2A current rating - Powers the system
- Wires - Used for the circuit
- Neodymium magnet N35, diam. 10mm, 2 pcs - Used the induce voltage to Hall sensor
- Circular sheet metal, diam 15mm, 2 pcs - Connects the magnet to curtains
- Standoff Male/Female, round, diam. 8mm length 50mm, 1 pc - Used to hold the Hall sensor close enough to the magnet on the curtain
Code to run the motor via Wifi
Implemented Code Block#include <SPI.h> #include <WiFiNINA.h> char ssid[] = "aalto open"; char pass[] = ""; int status = WL_IDLE_STATUS; WiFiServer server(80); //steppermotor pins int stepPin = 2; // The pin number for the step signal int dirPin = 3; // The pin number for the direction signal int enPin = 4; // The pin number for the enable signal int hallPin = 7; //pin for hall sensor int stepDelay = 800; //delay between steps int RevolutionSteps = 200 * 5; //for calculating steps to make one revolution bool hallState; //state of hall monitpr signal bool isUp; //state to know if the curtain is up or down int step_count = 0; //for calculating curtain position at any time void setup() { //declaring pins as input or output pinMode(stepPin, OUTPUT); pinMode(dirPin, OUTPUT); pinMode(enPin, OUTPUT); pinMode(hallPin, INPUT); digitalWrite(enPin,HIGH); //diable current flow to stepper motor Serial.begin(9600); // check for the WiFi module: if (WiFi.status() == WL_NO_MODULE) { Serial.println("Communication with WiFi module failed!"); // don't continue while (true); } String fv = WiFi.firmwareVersion(); if (fv < WIFI_FIRMWARE_LATEST_VERSION) { Serial.println("Please upgrade the firmware"); } // attempt to connect to WiFi network: while (status != WL_CONNECTED) { Serial.print("Attempting to connect to Network named: "); Serial.println(ssid); // print the network name (SSID); // Connect to WPA/WPA2 network. Change this line if using open or WEP network: status = WiFi.begin(ssid, pass); // wait 10 seconds for connection: delay(10000); } server.begin(); // start the web server on port 80 printWifiStatus(); // you're connected now, so print out the status } void loop(){ WiFiClient client = server.available(); // listen for incoming clients if (client){ // if you get a client, Serial.println("new client"); // print a message out the serial port String currentLine = ""; // make a String to hold incoming data from the client while (client.connected()){ // loop while the client's connected if (client.available()){ // if there's bytes to read from the client, char c = client.read(); // read a byte, then Serial.write(c); // print it out the serial monitor if (c == '\n'){ // if the byte is a newline character // if the current line is blank, you got two newline characters in a row. // that's the end of the client HTTP request, so send a response: if (currentLine.length() == 0){ // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK) // and a content-type so the client knows what's coming, then a blank line: client.println("HTTP/1.1 200 OK"); client.println("Content-type:text/html"); client.println(); // the content of the HTTP response follows the header: client.print("<style>"); client.print(".container {margin: 0 auto; text-align: center; margin-top: 100px;}"); client.print("button {color: white; width: 100px; height: 100px;"); client.print("border-radius: 50%; margin: 20px; border: none; font-size: 20px; outline: none; transition: all 0.2s;}"); client.print(".up{background-color: rgb(196, 39, 39);}"); client.print(".down{background-color: rgb(39, 121, 39);}"); client.print(".up-step{background-color: rgb(196, 39, 39);}"); client.print(".down-step{background-color: rgb(39, 121, 121);}"); client.print("button:hover{cursor: pointer; opacity: 0.7;}"); client.print("</style>"); client.print("<div class='container'>"); client.print("<button class='up' type='submit' onmousedown='location.href=\"/UP\"'>UP</button>"); client.print("<button class='down' type='submit' onmousedown='location.href=\"/DOWN\"'>DOWN</button><br>"); client.print("<button class='up-step' type='submit' onmousedown='location.href=\"/UP-STEP\"'>UP-STEP</button>"); client.print("<button class='down-step' type='submit' onmousedown='location.href=\"/DOWN-STEP\"'>DOWN-STEP</button><br>"); client.print("</div>"); // The HTTP response ends with another blank line: client.println(); // break out of the while loop: break; } else { // if you got a newline, then clear currentLine: currentLine = ""; } } else if (c != '\r') { // if you got anything else but a carriage return character, currentLine += c; // add it to the end of the currentLine } //calls function to do action that we want if(currentLine.endsWith("GET /UP")){ roll_up(); } if(currentLine.endsWith("GET /DOWN")){ roll_down(); } if(currentLine.endsWith("GET /UP-STEP")){ roll_up_step(); } if(currentLine.endsWith("GET /DOWN-STEP")){ roll_down_step(); } } } // close the connection: client.stop(); Serial.println("client disconnected"); } } void roll_up(){ hallState = digitalRead(hallPin); // Read the hall effect sensor state digitalWrite(enPin, LOW); // Enable the motor driver while(step_count > 0){ // Loop while the target step count is greater than zero hallState = digitalRead(hallPin); // Read the hall effect sensor state if(hallState == 0){ // If the sensor detects the magnet, break out of the loop break; } if(isUp == 1){ // If the roller is already up, break out of the loop break; } digitalWrite(dirPin, HIGH); // Set the motor direction to up digitalWrite(stepPin, HIGH); // Trigger the motor to take a step delayMicroseconds(stepDelay); // Delay for the specified delay time digitalWrite(stepPin, LOW); // Set the motor back to its initial state delayMicroseconds(stepDelay); // Delay for the specified delay time step_count = step_count - 1; // Decrease the step count by one Serial.println(step_count); // Print the current step count to the serial monitor } digitalWrite(enPin, HIGH); // Disable the motor driver isUp = 1; // Set the roller state to up step_count = 0; // Reset the step count } void roll_down(){ hallState = digitalRead(hallPin); // Read the hall effect sensor state digitalWrite(enPin, LOW); // Enable the motor driver while(step_count <= 14000){ // Loop while the target step count is less than or equal to 14000 if(isUp == 0){ // If the roller is already down, break out of the loop break; } if(hallState == 0){ // If the sensor detects the magnet, break out of the loop break; } hallState = digitalRead(hallPin); // Read the hall effect sensor state digitalWrite(dirPin, LOW); // Set the motor direction to down digitalWrite(stepPin, HIGH); // Trigger the motor to take a step delayMicroseconds(stepDelay); // Delay for the specified delay time digitalWrite(stepPin, LOW); // Set the motor back to its initial state delayMicroseconds(stepDelay); // Delay for the specified delay time step_count = step_count + 1; // Increase the step count by one Serial.println(step_count); // Print the current step count to the serial monitor } digitalWrite(enPin, HIGH); // Disable the motor driver isUp = 0; // Set the roller state to down } //roll_up_step and roll_down_step does basically the same as two other functions but they dont take steps to go all the way, only little bit every time (can be changed how much) void roll_up_step(){ hallState = digitalRead(hallPin); digitalWrite(enPin, LOW); int i = 0; while(i <= 700){ hallState = digitalRead(hallPin); if(hallState == 0){ break; } digitalWrite(dirPin, HIGH); digitalWrite(stepPin, HIGH); delayMicroseconds(stepDelay); digitalWrite(stepPin, LOW); delayMicroseconds(stepDelay); i = i + 1; step_count = step_count - 1; Serial.println(step_count); } digitalWrite(enPin, HIGH); } void roll_down_step(){ hallState = digitalRead(hallPin); digitalWrite(enPin, LOW); int i = 0; while(i <= 700){ if(hallState == 0){ break; } digitalWrite(dirPin, LOW); digitalWrite(stepPin, HIGH); delayMicroseconds(stepDelay); digitalWrite(stepPin, LOW); delayMicroseconds(stepDelay); i = i + 1; step_count = step_count + 1; Serial.println(step_count); } digitalWrite(enPin, HIGH); } void printWifiStatus(){ // print the SSID of the network you're attached to: Serial.print("SSID: "); Serial.println(WiFi.SSID()); // print your board's IP address: IPAddress ip = WiFi.localIP(); Serial.print("IP Address: "); Serial.println(ip); // print the received signal strength: long rssi = WiFi.RSSI(); Serial.print("signal strength (RSSI):"); Serial.print(rssi); Serial.println(" dBm"); // print where to go in a browser: Serial.print("To see this page in action, open a browser to http://"); Serial.println(ip); }
- Mechanical design of the system
- Design of the system consist of motor housing (section 4.a.i), front cover(section 4.a.iii), back cover(section 4.a.iv), gearbox(section 4.a.ii), bearing housing(section 4.a.v). Additional pictures of the design is in pictures 6 and 7.
Motor housing is designed to as compact as possible to be as invicible as possible, but there has to be enought space for electronics, gearbox and the motor
Picture 1. Main dimensions of the motor housing
Design of the gearbox is from Thingiverse. Gearbox' ratio is 5:1 to upscale the torque level of the motor. Planetary gearbox is chosen for slim design.
Picture 2. Assembly view of planetary gearbox for Nema 17 stepper motor
- Front cover is connected to the housing by pressfit. Small side tabs are fitted between housing and the motor, tab on the bottom is inserted on the slot on the housing.
Picture 3. Main dimensions of the front cover of motor housing
Back cover is press fit the back side of the motor
Picture 4. Main dimensions of the back cover of motor housing
- 626 ZZ bearing is inserted to bearing housing. This housing is installed to curtain holder
- Design of the system consist of motor housing (section 4.a.i), front cover(section 4.a.iii), back cover(section 4.a.iv), gearbox(section 4.a.ii), bearing housing(section 4.a.v). Additional pictures of the design is in pictures 6 and 7.
Picture 5. Main dimensions of the bearing housing
Picture 6 Main electronic components of the motor on the PCB-board
Picture 7. Motor assembly mounted on wall and installed on curtain( cyan cylinder with small portion of curtain shown)
5. Constructing the system
The construction consisted of 3D-printing, soldering, drilling and mechanical assembly.
- 3D-printing
- motor housing(picture 1), front cover(picture 3), back cover (picture 4), gearbox(picture 2) and bearing housing (picture 5)
- Soldering
- Schematic picture of soldering is in "Electronics Schematic" - section
- Picture 6 shows locations of the main electronic components on the circuit board
- Drilling
- Drilling hole for wiring of the Hall sensor
- Hole (diam. 3mm) was drilled to motor housing (picture 7). Location of the drilling hole was 15mm from front of the housing and 10 mm from bottom left.
- Drilling hole for power
- Hole (diam. 3mm) was drilled to lower left corner of the back cover(picture 4)
- Drilling hole to curtain holder for the bearing housing
- Hole (diam. 21mm) was drilled the curtain holder for the bearing housing. Center of the hole was located to original attachment point of the curtains.
- Drilling hole to curtain end piece
- Hole(diam. 5.8mm, depth 8mm) was drilled to end piece of the curtains. Hole is for the attachment screw M6 from the motor. End piece of the curtain was utilized to avoid unnecessary 3D-printing. Option was to print conical shaped part, that could be installed to any curtain by pressfit.
- Drilling hole for wiring of the Hall sensor
- Mechanical assembly
1. Gearbox is put together as in picture 2. More detailed assembly process in www.thingiverse.com/thing:8460
2. Gearbox is assembled to motor by 4x M3x50 threaded rod
3. Electonics is put inside motor housing and hall sensor wiring is installed via drilled hole and put in the connectors on circuit board. Motor wiring is put to the connectors of the circuit board.
4. Front cover is installed to front part of the motor housing
5. 626 ZZ bearing is istalled in to the bearing housing by pressing it in
6. Bearing housing is installed in to the curtain holder (picture 7)
7. Curtain holder is installed via hole in the front part of the motor housing
8. Motor-gearbox subassembly is installed in to the front cover and M6 screw is inserted into bearing 626 ZZ
9. Motor-gearbox- subassembly is tightened to the housing using 4x M3 nuts
10. System is screwed to the wall using M4,M5 or M6 screws
11. End piece of the curtain is tightened to the screw by holding M6 screw still with pliers and rotating the curtain
12. Standoff is installed to wall by drilling small hole for male part of the standoff. Hole is located near the end of the curtain
13. Hall sensor is glued to the standoff
14. Magnet and circular sheet metal part is installed to lower portion and higher portion of the curtain to make sure curtain does not go above given limits
15. Back cover is installed to motor housing and 12V power supply line is guided from hole and tightened to connectors on the circuit board
1.1. Electronics Schematic
The following schematic illustrates the electronic connections used in our project to implement the DRV8825 stepper driver circuit with Arduino Nano