[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.
This is removed. (Nevermind the dirt.)
The little connecting springrod needs to removed as well.
The D-shaft in the middle is the thing the servo needs to move.
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!
Needs a little persuasion.
Cut the coupler with the little set screw from the little springrod.
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.
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.
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.
The servo lined up perfectly in the housing and with the D-shaft after I de-tabbed it.
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)
I put the connector back on the other side.
ELECTRICAL PART
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.
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!)
This makes it easy to connect the button box with the servo cable and both are powered.
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.
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
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 textOh yeah, some video:
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.
-
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.
-
@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:
-
First finished sensor!:
Obvious flaw:
Whole window
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.
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)
-
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(); } }```
Suggested Topics
-
Welcome
Announcements • • hek