Mechatronics Exercises

Workspace Navigation

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).

  1. Properties and restrictive elements are
    1. 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.

  2. List of components
    1. Nema 17 Stepper motor - Works as a motor of the system
    2. DRV8825 stepper motor driver - Driver runs the motor in controlled manner
    3. Arduino RP2040 micro controller with Wifi and bluetooth - Microcontroller is the brains of the system
    4. Hall sensor: TLE4905L DIGITAL HALL SENSOR UNIPOLAR SWITCH - Works as a limit switch if motor has lost its current position
    5. Capasitor, 50V 47uF - Protects motor when starting the system
    6. 683-RSZ bearings,3x7x3mm, 6 pcs - Installed to both sides of the planets of the gearbox
    7. 606-2RS bearing, 6x17x6mm, 1 pc - Installed to cover of the gearbox for M6 screw
    8. 626 ZZ bearing, 6x19x6mm, 1 pc - Installed in the bearing housing in curtains' holder
    9. M3 threaded rod, M3x50, 4 pcs - Connects motor, gearbox and front cover of the motor housing
    10. M3 Cap head screw, M3x16, 3 pcs - Connects carrier and planets together with 683-RSZ bearings
    11. M3 nut, 7 pcs - Installed to threaded rods and M3 cap head screws
    12. M6 bolt, M6x50, 1 pc - Connects Motor/gearbox to curtain adapter
    13. PCB-board, 70x50mm, 1 pc - All the electronics are soldered to PCB-board
    14. 12V power supply with 2A current rating - Powers the system
    15. Wires - Used for the circuit
    16. Neodymium magnet N35, diam. 10mm, 2 pcs - Used the induce voltage to Hall sensor
    17. Circular sheet metal, diam 15mm, 2 pcs - Connects the magnet to curtains
    18. Standoff Male/Female, round, diam. 8mm length 50mm, 1 pc - Used to hold the Hall sensor close enough to the magnet on the curtain
  3. 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);
    }


  4.  Mechanical design of the system
    1. 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.
      1. 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


      2. 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




      3. 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


      4.  Back cover is press fit the back side of the motor


        Picture 4. Main dimensions of the back cover of motor housing
      5. 626 ZZ bearing is inserted to bearing housing. This housing is installed to curtain holder


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.

  1. 3D-printing
    1. motor housing(picture 1), front cover(picture 3), back cover (picture 4), gearbox(picture 2) and bearing housing (picture 5)
  2. Soldering
    1. Schematic picture of soldering is in "Electronics Schematic" - section
    2. Picture 6 shows locations of the main electronic components on the circuit board
  3. Drilling
    1. Drilling hole for wiring of the Hall sensor
      1. 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.
    2. Drilling hole for power
      1. Hole (diam. 3mm) was drilled to lower left corner of the back cover(picture 4)
    3. Drilling hole to curtain holder for the bearing housing
      1. 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.
    4. Drilling hole to curtain end piece
      1. 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.
  4. 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

  • No labels
  File Modified
PNG File image2023-3-13_23-29-37.png Mar 13, 2023 by Jusa Martin
PNG File image2023-3-13_23-29-58.png Mar 13, 2023 by Jusa Martin
PNG File image2023-3-13_23-32-56.png Mar 13, 2023 by Jusa Martin
PNG File image2023-3-13_23-36-58.png Mar 13, 2023 by Jusa Martin
PNG File image2023-3-19_19-11-27.png Mar 19, 2023 by Jusa Martin
PNG File image2023-4-20_10-13-10.png Apr 20, 2023 by Jusa Martin
PNG File image2023-4-20_11-15-55.png Apr 20, 2023 by Jusa Martin
PNG File image2023-4-20_11-20-31.png Apr 20, 2023 by Jusa Martin
PNG File image2023-4-20_11-30-24.png Apr 20, 2023 by Jusa Martin
PNG File image2023-4-20_11-7-4.png Apr 20, 2023 by Jusa Martin
PNG File image2023-4-20_12-16-54.png Apr 20, 2023 by Jusa Martin
PNG File image2023-4-20_12-17-35.png Apr 20, 2023 by Jusa Martin
PNG File image2023-4-20_12-35-20.png Apr 20, 2023 by Jusa Martin
PNG File image2023-4-20_12-9-22.png Apr 20, 2023 by Jusa Martin
PNG File image2023-4-23_22-36-11.png Apr 23, 2023 by Jusa Martin
PNG File image2023-4-23_22-55-2.png Apr 23, 2023 by Jusa Martin
PNG File image2023-4-23_23-51-55.png Apr 23, 2023 by Jusa Martin
PNG File image2023-4-23_23-52-12.png Apr 23, 2023 by Jusa Martin
PNG File image2023-4-23_23-52-40.png Apr 23, 2023 by Jusa Martin
PDF File Schematic_Stepper Motor Driver_2023-04-23.pdf Apr 23, 2023 by Lamsal Sanyog
Multimedia File VID_20230413_115314.mp4 Apr 23, 2023 by Jusa Martin
Multimedia File VIDEO-2023-03-16-16-21-18.mp4 Apr 23, 2023 by Roope Ruokonen