[contest] Yet another servo blind control project



  • Hello all.

    POST UPDATED 13.2.2015

    UPDATE 2: Noticed there's a competition going on and as I think this project qualifies, I'm entering it. So there.

    I've been following this site for a while now and finally decided to register and share my little project.

    I've had a Vera for ages which I've been using for my zwave lights. I really wanted to be able to control my curtains and blinds with it as well, but since it's next to impossible to get moderately priced motors for home automation here in Finland, I started exploring DIY solutions.

    I actually thought I had an original idea about using an Arduino and a servo to tilt my blinds but it turned out I'm late to the party.

    Anyways, here's my project so far and why it isn't finished: (edit: It is now...)

    First of all, blinds in Finland are almost always what the Americans call mini-blinds because we put them between the outer and inner windows and they are almost identical everywhere. This means my solution should work for most Finns... 😉

    THE MECHANICAL PART:

    The blinds are adjusted with a rod directly linked to the shaft that tilts the slats.

    IMG_8043.jpg

    This is removed. (Nevermind the dirt.)

    IMG_8044.jpg

    The little connecting springrod needs to removed as well.

    IMG_8050.jpg

    The D-shaft in the middle is the thing the servo needs to move.

    IMG_8049.jpg

    So we need some way to connect the servo spline to the shaft. There are shaft couplers available for servos but they cost about three times as much as the servos! So I swiped an idea from the guys making sail winch servos. Bobbins!

    IMG_8054.jpg
    IMG_8056.jpg

    Needs a little persuasion.

    IMG_8057.jpg
    IMG_8058.jpg
    IMG_8060.jpg
    IMG_8061.jpg

    Cut the coupler with the little set screw from the little springrod.

    IMG_8062.jpg
    IMG_8064.jpg

    I was going to glue this together but I couldn't find any, so I soldered it. Don't know how well that is going to hold.

    IMG_8065.jpg

    I installed the coupler to the opposite side so that the servo won't interfere with the strings used to pull up the blinds. The D-shaft slides with little force.

    IMG_8067.jpg

    I adjusted the slats to where I think they are closed (slat outer edge down). Some people have different views about the matter but they're just wrong 🙂 I didn't attach the servo just yet.

    IMG_8074.jpg

    The servo lined up perfectly in the housing and with the D-shaft after I de-tabbed it.

    IMG_8069.jpg
    IMG_8070.jpg

    After this, I gently disconnected the wires from the servo extension cable connector so I could thread it back through the rod hole. (The string goes through the lower hole in a spring tunnel)

    IMG_8075.jpg
    IMG_8077.jpg
    IMG_8078.jpg

    I put the connector back on the other side.

    IMG_8087.jpg

    ELECTRICAL PART

    UPDATE The finished sensor

    I really wanted to have a physical button to adjust the blinds so I put one in this temporary box along with a 5v pro mini.

    IMG_8032.jpg
    IMG_8031.jpg
    IMG_8034.jpg

    I spliced in to another servo extension cable 5v from a phone charger. (Note to self: don't cut the wires so damn close to the connector!)

    IMG_8035.jpg

    This makes it easy to connect the button box with the servo cable and both are powered.

    IMG_8040.jpg

    The final version will have a DC socket at the junction so I can just run an easily disconnectable plug to every window. The holes are always on the hinge side so the window can be opened even without unplugging. I'll also wrap everything nicely in white heat shrink tubing and the box will be nicer, too.

    THE CODE PART

    This one was/is the most difficult for me because I had exactly zero experience with C++. Or any C. I've used a bit of python and some simple shell scripts but this was new. Anyways, I managed to write this:

    **UPDATE:**This is just test code for use without the Mysensors stuff. Scroll down for working code.

    link text

    Why this is bit complicated is because I wanted the button to do two things:

    • Press once; toggle open/close

    • Keep pressing the button; sweep back and forth until button is released and stop there.

    At the moment it does just that.

    I connected the servo to the shaft once I figured out with the button which way was closed and which way was open. 180 degrees was exactly the difference between open and closed.

    Now some of you may be thinking, "what the hell has this got to do with mysensors?" That's why the title is tagged [WIP]. I've been waiting for my NRF24L01+ -modules to arrive for a month and finally today they arrived.

    And they're not NRF24L01+ -modules.

    BUY YOUR RADIOS FROM THE MYSENSORS EBAY STORE LINK

    IMG_8093.jpg

    They were sold as NRF24L01+'s but they definitely are not. So now I have to wait again.

    Meanwhile, could someone tell me if they see something horribly wrong with this sketch I've been planning to use once I do get the radios? I can't really test it. (This works well now)

    UPDATE: Code is VERY much refactored and improved (works well)
    link text

    Oh yeah, some video:

    link text

    link text

    Sorry for the long post.



  • @slarti Nice post with great pictures! We seem to have the same blinds in Sweden as well.



  • Thanks!

    I guess the blinds are the same in the nordic countries.

    I'm really pissed off waiting for the radios, can't really do anything without them... 😞

    ... except for the extremely innovative 5v rail.

    IMG_8100.jpg


  • Admin

    @slarti

    Haha..nice! Curtains are great for hiding stuff 🙂



  • @hek Indeed, as is cable raceway. I think I've bought a kilometer of the stuff and it's never enough. Improves the WAF, though.

    I'm surprised how well the little Nokia charger can handle the 4 servos and arduinos, although I haven't tried running all servos at once. I have a bigger 5v power supply in reserve but I haven't needed it yet while testing.

    I'm really frustrated with the radios I have because they seem like the real thing, but I'll post about them in troubleshooting.


  • Hero Member

    @slarti LOVE IT!! Very nice job!



  • A little bit of progress. Got some functional radios today and I've been trying to fit everything in the control boxes. I think I have an ok design but once I started soldering I realised that the protoboard I was using wasn't dot matrix like it said on the box but copper tracks 😡

    Oh well, I'll have to get something else tomorrow...

    Some mock up pics:

    IMG_8119.jpg
    IMG_8120.jpg
    IMG_8121.jpg
    IMG_8122.jpg



  • First finished sensor!:
    IMG_8136.jpg
    IMG_8135.jpg
    IMG_8134.jpg

    Obvious flaw:
    IMG_8137.jpg

    Whole window
    IMG_8139.jpg

    What is the best way to disable the power LED? Desolder it?



  • @slarti said:

    What is the best way to disable the power LED? Desolder it?

    You can cut the pcb trace leading to this led.



  • After soldering three ratsnests like the one in the previous post, I decided there has to be a simpler way to do things. So after a day of watching eagle cad tutorials and messing about with the software I came up with a board of my own.

    top_osh.png
    bot_osh.png

    I didn't want to add any stuff I didn't need except for a pin header for raw input, A0-breakout and a choice of 5V/3.3V output for A0 and D4 with a jumper. I also don't want to solder every pin on the pro mini so I can use them possibly elsewhere.

    Am I shooting a fly with a cannon? Do I need something else on the board? DirtyPCB and other chinese manufacturers are closed at the moment but are there alternatives besides OSHPARK?

    BTW, three arduinos and servos seem to be the maximum simultaneous capacity for the little Nokia charger. They all work but get a bit flaky after a few tests. We'll see what happens when I add the fourth 😉



  • Ok, I'm calling this one finished. I got all the wiring done and my sketch is working well enough (I'll update the first post). The result doesn't look too bad, either.

    I don't know if the little charger doesn't have enough power for the four arduinos and servos at once or if the radio network gets clogged up when Vera tries to control them all at the same time, but they work well as a group when I add a 500ms delay between each command. Here's the lua script I've put in a scene:

    local SSID = "urn:upnp-org:serviceId:Dimming1" 
    local value = luup.variable_get(SSID, "LoadLevelStatus", 59)
    windows = {59, 58, 54, 61}
    if (value == "0") then
      for i=1,4 do
        luup.call_action(SSID, "SetLoadLevelTarget", {newLoadlevelTarget = "100"}, windows[i])
        luup.sleep(500)
      end 
    else
      for i=1,4 do
        luup.call_action(SSID, "SetLoadLevelTarget", {newLoadlevelTarget = "0"}, windows[i])
        luup.sleep(500)
      end
    end
    

    It just looks up the status of the first window and then changes all positions to the exact opposite of that single window.

    I'll update the first post with a video of them being controlled with a zwave fibaro switch that controls another hidden fibaro switch which triggers the lua script in Vera (that's not complicated at all 😉 ).

    All in all, this ended up costing almost as much as a commercial solution if you count all the stuff I ordered from ebay and the tools I had to buy. On the other hand I now own the tools and I have a ton of leftover material for several more windows and sensors and now I know some C++, lua and even eagle CAD! Very happy with the end result. Thanks to the MySensors team for making it possible! 👍

    Here's the parts list (price/window):

    • Servo extension cables (2pcs 0.88€)

    • Servos (1pc 2.78€)

    • Bobbins (1pc 0.09€)

    • Buttons (1pc 0.70€)

    • Heat shrink tube (maybe 0.10€ worth)

    • Cat6 cable (solid core) (maybe 0.10€ worth)

    • Protoboard (1pc 0.20€)

    • Project box (1pc 1.44€)

    • Cable raceway (about 1€)

    • 3.3V regulators (1pc 0.12€)

    • 4,7 uF capacitors (1pc 0.16€)

    • various bits and pieces I can't think of now (can't think of a price, either)

    • 5v Arduino Pro Mini clone (1pc 2€)

    • NRF24L01+ -radio (1pc 0.70€)

    Grand total/window : 10.27€



  • Excellent project report, Thanks!
    It made soo easy to order the required parts 🙂
    I suppose I should post my project, once it's been done, too... these things just tend to take their time (ofter years---to infinity).



  • Hi,

    Your project is very cool !
    Do you known if it is possible to control it with a raspberry pi and domoticz ?
    Thank's



  • @exosteph I would think so since these are just MySensors actuator nodes.

    The sketches are made using MySensors 1.4 libraries so they might not work with 1.5. I've been really busy lately and haven't had any time for this stuff 😢

    I did make some helper boards earlier to make it easier to solder these sensors (nevermind the horrible soldering)

    IMG_20150314_195847.jpg
    IMG_20150319_173009.jpg IMG_20150319_172532.jpg IMG_20150319_173020.jpg



  • Darn. When I connect the servo to the setup the Arduino loses it. Any idea how to tackle this? I've tried various capacitors all over the places😑



  • @Nuubi Can you show your connections?



  • It was, in the end and again, a power issue. I didn't mention that I included an RGB control to the node, so I'm feeding the whole nest with a 12v low grade supply. And the servo gets its power via Nano -- no external regulator for that.

    All in all, caps in the power supply, radio, nano 5V and 3.3V, aaaallll works now 🙂

    BTW, now that you've had your blinds automated, have you been happy with them? All blinds MySensorised yet?



  • @Nuubi Yes, I've been happy with them but MySensoring the rest of them is on hold. I've been extremely busy with a new job for a long time and I was supposed to do a few more improvements and tweaks before doing the rest of them. Also, my tinkering space (the kitchen table) is always under heavy pressure from other activities. Maybe I can get some time during the winter.

    TL;DR I've been lazy



  • My ethernet gateway died and I made another one on a Raspberry Pi so I updated all of my sensors from 1.4 libraries to 2.2. Here's the updated sketch for these servo blind control nodes:

    // A sketch to control a servo with a button and MySensors messages
    //#define MY_DEBUG
    
    // Enable and select radio type attached
    #define MY_RADIO_NRF24
    //#define MY_RADIO_RFM69
    
    #include <SPI.h>
    #include <MySensors.h>
    
    #include <Servo.h>
    #include <Button.h>        	//https://github.com/JChristensen/Button
    
    #define BUTTON_PIN 4       	//Connect a tactile button switch (or something similar)
                                //from Arduino pin 4 to ground.
    #define PULLUP true        	//To keep things simple, we use the Arduino's internal pullup resistor.
    #define INVERT true        	//Since the pullup resistor will keep the pin high unless the
                               	//switch is closed, this is negative logic, i.e. a high state
                               	//means the button is NOT pressed. (Assuming a normally open switch.)
    #define DEBOUNCE_MS 20     	//A debounce time of 20 milliseconds usually works well for tactile button switches.
    #define LONG_PRESS_PERIOD 700   //How long to keep button pressed until sweeping starts
    
    #define MAX_DEGREES 180         //Servo limits. Whatever works for you.     
    #define MIN_DEGREES 0
    
    #define CHILD_ID 3
    
    Button myBtn(BUTTON_PIN, PULLUP, INVERT, DEBOUNCE_MS);    //Declare the button
    
    Servo myservo;
    enum {DECREASING, INCREASING};      //On a servo mounted on the left, with outer slat edge down as closed,
                                        // closing is going toward 180, opening toward 0 (on my fake futaba s3003's)
    
    boolean invertConversions = true;   // false if opening toward 180
    boolean servoDirection = INCREASING;     //A variable that keeps the current servo direction
    int servoPin = 3;                  
    int servoPosition;
    int servoSpeed = 1;                 // The bigger, the faster. 1=slow 5=fast
    
    
    MyMessage msg(CHILD_ID, V_PERCENTAGE);
    
    
    
    void setup(void)
    {
      
    }
    
    void presentation()  {
      // Send the sketch version information to the gateway and Controller
      sendSketchInfo("Servo", "1.3");
    
      // Register all sensors to gw (they will be created as child devices)
      present(CHILD_ID, S_COVER);
    }
    
    void loop(void)
    {
        myBtn.read();                    		//Read the button
    
        if (myBtn.wasReleased()){                   //If the button was pressed once sweep to end of current direction
            SweepToDirectionEnd();
            send(msg.set(ConvertDegToPercent(servoPosition)));
        }
        if (myBtn.pressedFor(LONG_PRESS_PERIOD)){   //If the button is held down the servo will start to sweep
            SweepUntilStop();
            send(msg.set(ConvertDegToPercent(servoPosition)));
        }
    }
    
    
    int ConvertPercentToDeg(int percent)
    {
        int degree;
        if (invertConversions)
            degree = map(percent, 0, 100, MAX_DEGREES, MIN_DEGREES);
        if (!invertConversions)
            degree = map(percent, 0, 100, MIN_DEGREES, MAX_DEGREES);
        return degree;
    }
    
    
    int ConvertDegToPercent(int degree)
    {
        int percent;
        if (invertConversions)
            percent = map(degree, MAX_DEGREES, MIN_DEGREES, 0, 100);
        if (!invertConversions)
            percent = map(degree, MIN_DEGREES, MAX_DEGREES, 0, 100);
        return percent;
    }
    
    void receive(const MyMessage &message) {
        myservo.attach(servoPin);
        if (message.type==V_PERCENTAGE) {
            int val = message.getInt();
            SweepToPosition(ConvertPercentToDeg(val)); //In this case the value has to be inverted because 0 = open
            send(msg.set(val));
        }
        else if (message.type==V_STATUS) {
            if (message.getInt() == 1){
            SweepToPosition(ConvertPercentToDeg(100));
            send(msg.set(100));
          }
            else if(message.getInt() == 0) {
            SweepToPosition(ConvertPercentToDeg(0));
            send(msg.set(0));
          }
        }
        else
            myservo.detach();
    }
    
    
    
    
    void ServoMoveUp()
    {
        if ((myservo.attached()) && servoPosition < MAX_DEGREES){
            servoDirection = INCREASING;
            servoPosition += servoSpeed;
            myservo.write(servoPosition);
            delay(10);
            Serial.print("Servo Position: ");
            Serial.println(servoPosition);
        }
        if (!myservo.attached()){      
            Serial.println("Servo stopped while moving toward MAX, direction unchanged");
            delay(100 * servoSpeed);
        }    
        if (servoPosition >= MAX_DEGREES){
            Serial.println("MAX reached, changing direction toward MIN");
            servoDirection = DECREASING;
            delay(100 * servoSpeed);			// Wait for the last movement to finish
        }
    }
    
    
    void ServoMoveDown()
    {
        if ((myservo.attached()) && servoPosition > MIN_DEGREES){
            servoDirection = DECREASING;
            servoPosition -= servoSpeed;
            delay(10);
            myservo.write(servoPosition);
            Serial.print("Servo Position: ");
            Serial.println(servoPosition);
        }
        if (!myservo.attached()){
            Serial.println("Servo stopped while moving toward MIN, direction unchanged");
            delay(100 * servoSpeed);
        }    
        if (servoPosition == MIN_DEGREES){
            Serial.println("MIN reached, changing direction toward MAX");
            servoDirection = INCREASING;
            delay(100 * servoSpeed);
        }
    }
    
    
    void SweepToDirectionEnd()
    {
        myservo.attach(servoPin);
        if (servoDirection == INCREASING){
            Serial.println("Going to MAX and stopping there");
            while (servoPosition < MAX_DEGREES){
                ServoMoveUp();
            }
            delay(20 * servoSpeed);
            myservo.detach();
        }
        else if (servoDirection == DECREASING){
            Serial.println("Going to MIN and stopping there");
            while (servoPosition > MIN_DEGREES){
                ServoMoveDown();
            }
            delay(20 * servoSpeed);
            myservo.detach();
        }
    }
    
    
    void SweepUntilStop()
    {
        myservo.attach(servoPin);
        while (myBtn.isPressed()){
            myBtn.read();
            if (myBtn.isReleased())
                myservo.detach();
            if (servoDirection == INCREASING)
                ServoMoveUp();
            if (servoDirection == DECREASING)
                ServoMoveDown();
        }
    }
    
    
    void SweepToPosition(int destination)
    {
        if (abs(destination - servoPosition) >= servoSpeed)    //Don't move if destination is close to position
            myservo.attach(servoPin);
        if (destination > servoPosition && myservo.attached()){
            Serial.print("Going to ");
            Serial.print(destination);
            Serial.println(" and stopping there");
            while (servoPosition < destination){
                ServoMoveUp();
            }
            delay(20 * servoSpeed);
            myservo.detach();
        }
        if (destination < servoPosition && myservo.attached()){
            Serial.print("Going to ");
            Serial.print(destination);
            Serial.println(" and stopping there");
            while (servoPosition > destination){
                ServoMoveDown();
            }
            delay(20 * servoSpeed);
            myservo.detach();
        }
    }```

 

332
Online

7.4k
Users

8.3k
Topics

89.6k
Posts