This instructable is a detailed guide on how to build an automated controller to run a motor that can operate the opening and closing of blinds. The controller enables the blinds to open and close based on a schedule, room temperature and out door luminosity. The controller settings can be set up via a web UI which enables a user to open or close the blinds manually or place them on a schedule with certain parameters. 

This project will guide you through creating simple circuits for the sensors, hacking the motor shield so it can be stacked with the arduino and ethernet shield and implementation of the code (note all code is provided and is fully commented). The project is laid out in the following manner. First the controller and system will be constructed. Then the logic behind the code will be explained. The last page will contain the code and link to download so it can be installed on the arduino.

So I hope you enjoy this instructable, gain from it and most importantly have fun.

Table of contents:

1.Requirements and parts
2.Wiring the sensors.
3.Wiring the arduino and shields.
4.Hacking the motor shield.
5.Code logic.

Step 1: Requirements and parts

This project is fairly simple to create and set up since the most difficult part, the code, is fully provided. The setting up the arduino and shields is fairly straight forward. Below is a list of parts you will need for this project. I have also listed the prices for these parts if you need do not have them. Most of these can be ordered from Adafruit, Sparkfun or the robotshop.

1. Arduino ($30)
2. Ethernet shield ($50)
3. Motor Shield ($20)
4. Analog temperature (TMP36)  sensor ($2)
5. Photocell ($1)
6. 20 pin 0.1" Female Headers ($3)
7. Breadboarding Wire Bundle (6)
8. Breadboard friendly 2.1mm DC Jack ($1)
9. 12 V Stepper Motor (14)
10. 100 ohm resistor
11. MicroSD card (1gb card is good and can be found for as little as $2).
12. 2 Full breadboards ($14)
13. 12V AC adapter. (powers the motor only)
14. battery pack for the arduino

Remember, all these parts can be used for other projects, so don't worry too much about the cost because you'll be able to use them in many more projects, unless of course you are gonna use this as a permanent installment.

Equipment needed
1.Soldering iron
2.Soldering Fan
3. Small screwdriver.
<p>Let me know If this works , have not tried yet !</p><p>/*</p><p>Automated Blinds Controller<br>Programmed by Sarbjeet Singh<br>Last revision: August 30, 2012<br>*/<br>#include &lt;SPI.h&gt;<br>#include &lt;Ethernet.h&gt;<br>#include &lt;Time.h&gt;<br>#include &lt;AFMotor.h&gt;<br>#include &lt;SD.h&gt;<br>// Enter a MAC address and IP address for your controller below.<br>// The IP address will be dependent on your local network:<br>byte mac[] = { 0x90, 0xA2, 0xDA, 0x0D, 0x0D, 0xE0 };<br>IPAddress ip(192,168,0,10); //&lt;&lt;&lt; ENTER YOUR IP ADDRESS HERE!!!<br>// Initialize the Ethernet server library<br>// with the IP address and port you want to use <br>// (port 80 is default for HTTP):<br>EthernetServer server(80);<br><br>byte mem0=0; <br>byte mem1=0; //variables to set transition state<br>byte TemPin=0; //Input Pin for Temperature sensor<br>byte photocellPin=1; //Input Pin for Photocell<br>byte stepperRevolution=200; //number of steps taking for stepper to make on revolution. <br>byte limitRev=5;//number of revolutions needed to open or close blind. <br>byte NumberOfRevsFor=0; //Counter for number of revolutions completed by stepper motor going forward.<br>byte NumberOfRevsBack=limitRev; //Counter for number of revolutions completed by stepper motor in reverse.<br>AF_Stepper motor(200,2); //200 step/rev motor connect to M3 and M4 on motor shield <br>File myFile; <br>String value;<br>byte SDreadAtStart=0;<br>byte openHour,closeHour,ctltemp, OvRCtl=0, timeHour, timeMin; <br>int Luminosity; //variable which sensor luminosity reading is compared against. <br>void setup()<br>{ <br> Serial.begin(9600); <br> pinMode(10, OUTPUT); //set up reading and writing to SD card<br> SD.begin(4); <br> motor.setSpeed(250); //set motor 200 rpm.<br> // start the Ethernet connection and the server:<br> Ethernet.begin(mac, ip); <br> server.begin(); <br> //SDreader(&quot;BlindCtl/Settings/Settings.txt&quot;); //read values in from Time file. Hour and minute values are saved temporarily saved to Luminosity and ctltemp<br> //setTime(timeHour,timeMin,0,10,7,2012); //set current time on arduino <br> delay(1000); //Give ethernet and sensors time to set up. <br>}<br>void loop()//main <br>{ <br> byte SDread; <br> SDread=EthernetClients(); //checks to see if client is connected <br><br> if(SDreadAtStart==0||SDread==1)<br> { <br> SDreader(&quot;BlindCtl/Settings/Settings.txt&quot;); //reads values from Settings.txt<br> setTime(timeHour,timeMin,0,10,7,2012); <br> SDreadAtStart=1; <br> } <br> //Serial.println(Luminosity); <br> motors(Luminosity,ctltemp,openHour,closeHour); <br>}<br>void motors(int luminosity, byte ctltemp, byte openHour, byte closeHour)//(byte Open, byte Close)<br>{<br> // Serial.println(F(&quot;motors is started&quot;));<br> time_t t=now();//set current time to variable t<br> byte curTime=hour(t); //saves the current hour<br> Serial.println(curTime); <br> //String readFile, value; <br> analogRead(TemPin);<br> delay(10);<br> int temRead=analogRead(TemPin); //reads in from temperature sensor<br> float temVoltage=temRead*5.0;<br> temVoltage/=1024.0; <br> float temperature=(temVoltage-0.5)*100;<br> delay(10); //delay to let voltage settle before reading from photocell<br><br> analogRead(photocellPin);<br> delay(10);<br> int photocellReading=analogRead(photocellPin);//reads the photocell value<br> delay(10);<br> if(OvRCtl==1)//checks for variable that indicates if blinds should auto open and then sets requirements to low. <br> {<br> luminosity=200; <br> ctltemp=temperature+5;<br> openHour=0;<br> closeHour=24; <br> }<br> else if(OvRCtl==2)//checks variable to see if override for close is enables and then sets requirements high. <br> {<br> luminosity=999; <br> ctltemp=25;<br> openHour=0;<br> closeHour=24; <br> }<br> else<br> {<br> if(luminosity&lt;=200||luminosity&gt;=1000)//error checks to make sure user has not entered values outside of scope.<br> luminosity=600; <br> if(ctltemp&lt;=5||ctltemp&gt;=60)<br> ctltemp=25; <br> if(openHour&lt;=(-1)||openHour&gt;=25)<br> openHour=13;<br> if(closeHour&lt;=(-1)||closeHour&gt;=25)<br> closeHour=20;<br> } <br><br> if(mem0==0&amp;&amp;mem1==0)//State one where blinds are closed<br> { <br> // Serial.print(&quot;Blinds are closed \n&quot;);<br> if((photocellReading&gt;=luminosity)&amp;&amp;(curTime&gt;=openHour)&amp;&amp;(curTime&lt;closeHour)&amp;&amp;(temperature&lt;=ctltemp))//If all three of these conidtions are met, blinds will move into opening state. <br> { <br> mem0=1;<br> }<br> }<br><br> else if(mem0==1&amp;&amp;mem1==0)//Moving forward state<br> { <br> //Serial.print(&quot;Blinds are opening \n&quot;); <br> if(NumberOfRevsFor==limitRev)//checks to see if blinds are fully open and if so, switches to fully open state. <br> { <br> mem1=1;<br> mem0=1; <br> } <br> else if((photocellReading&gt;=luminosity)&amp;&amp;(curTime&gt;=openHour)&amp;&amp;(curTime&lt;closeHour)&amp;&amp;(temperature&lt;=ctltemp))//&amp;&amp;(NumberOfRevsFor!=TravelForward))//confirms that opening condition is still present and continues to move motor forward<br> {<br> motor.step(stepperRevolution, FORWARD, DOUBLE); <br> motor.release();<br> NumberOfRevsFor++; <br> NumberOfRevsBack--;<br><br> } <br> else if(temperature&gt;ctltemp||curTime&gt;=closeHour||photocellReading&lt;luminosity)//switches state to move back in closing condition has become true<br> { <br> mem1=1;<br> mem0=0; <br> } <br><br> }<br><br> else if(mem0==1&amp;&amp;mem1==1)//Full Open position<br> {<br> // Serial.print(&quot;Blinds are open \n&quot;);<br> if(temperature&gt;ctltemp||curTime&gt;=closeHour||photocellReading&lt;luminosity)//condition to move into closing state<br> {<br> mem1=1;<br> mem0=0; <br> } <br> }<br><br> else if(mem0==0&amp;&amp;mem1==1)//Blinds closing state<br> { <br> //Serial.print(&quot;Blinds are closing \n&quot;); <br><br> if(NumberOfRevsBack==limitRev)//switches to closed state if blinds are fully closed. <br> {<br> mem1=0;<br> mem0=0; <br> } <br> else if((temperature&gt;ctltemp||curTime&gt;=closeHour||photocellReading&lt;luminosity))//&amp;&amp;NumberOfRevsBack!=TravelBack)//checks for closing conditions and then proceeds to close blinds. <br> {<br> motor.step(stepperRevolution, BACKWARD, DOUBLE);<br> motor.release(); <br> NumberOfRevsBack++;<br> NumberOfRevsFor--;<br><br> } <br> else if(photocellReading&gt;=luminosity&amp;&amp;curTime&gt;=openHour&amp;&amp;curTime&lt;closeHour&amp;&amp;temperature&lt;=ctltemp)//if conditions for blinds opening become true, switches to blind open state<br> { <br> mem1=0;<br> mem0=1; <br> }<br> }<br> Serial.print(mem0);<br> Serial.print(mem1);<br> return; <br>}<br>byte EthernetClients()//web clients that takes what the user sets and saves them to teh SD card<br>{<br> boolean stopreading=0,variablePresent=0,storevalue=0,timeOn=0,clientC=0, equalsCount=0, SDread=0;<br> String readURL;<br> delay(10); <br> EthernetClient client = server.available(); <br> //Serial.println(&quot;Eth&quot;); <br> if (client) <br> { <br> // an http request ends with a blank line<br> boolean currentLineIsBlank = true;<br> //Serial.println(&quot;Client available&quot;); <br> Serial.println(F(&quot;Client&quot;)); <br><br> while (client.connected()) {<br> if (client.available()) {<br><br> char c = client.read();<br> if(c=='?')<br> {<br> variablePresent=1; <br> } <br><br> if (c=='z'){//stops reading into the string once end of header is reached. <br> stopreading=1;}<br> else if(c!='z' &amp;&amp; stopreading==0&amp;&amp;variablePresent==1)//reads in GET from the HTTP header<br> {<br> readURL+=c;<br> //Serial.println(readURL); <br> }<br><br> //delay(1000);<br> if(stopreading==1&amp;&amp;variablePresent==1&amp;&amp;storevalue==0)//reads stored GET header to extract variables<br> { <br><br> for(int i=0; i&lt;=(readURL.length()-1); i++)<br> { <br> if(readURL[i]=='=')<br> {<br> int j=i+1;<br> while(readURL[j]!='&amp;'&amp;&amp;readURL[j]!='x')<br> {<br> value+=readURL[j];<br> j++;<br> } <br><br> if(value==&quot;Open&quot;)<br> OvRCtl=1; <br> if(value==&quot;Close&quot;)<br> OvRCtl=2; <br><br> if(equalsCount==0&amp;&amp;value==&quot;Time&quot;){<br> timeOn=1;<br> OvRCtl=0;}<br> else if(value!=&quot;Open&quot;&amp;&amp;value!=&quot;Close&quot;&amp;&amp;equalsCount==0){<br> //timeOn=0; <br> SD.remove(&quot;BlindCtl/Settings/Settings.txt&quot;);<br> myFile= SD.open(&quot;BlindCtl/Settings/Settings.txt&quot;,FILE_WRITE); <br> myFile.print(F(&quot;Luminx&quot;));<br> myFile.print(value); <br> myFile.print(&quot;z&quot;);<br> myFile.close(); <br> }<br> if(equalsCount==1)<br> { <br> myFile= SD.open(&quot;BlindCtl/Settings/Settings.txt&quot;,FILE_WRITE); <br> myFile.print(F(&quot;Tempx&quot;));<br> myFile.print(value); <br> myFile.print(&quot;z&quot;);<br> myFile.close(); <br> } <br> if(equalsCount==2)<br> { <br> myFile= SD.open(&quot;BlindCtl/Settings/Settings.txt&quot;,FILE_WRITE); <br> myFile.print(F(&quot;OpenHourx&quot;));<br> myFile.print(value); <br> myFile.print(&quot;z&quot;); <br> myFile.close(); <br> }<br> if(equalsCount==3)<br> { <br> myFile= SD.open(&quot;BlindCtl/Settings/Settings.txt&quot;,FILE_WRITE); <br> myFile.print(F(&quot;CloseHourx&quot;));<br> myFile.print(value); <br> myFile.print(&quot;z&quot;); <br> myFile.close();<br> } <br> if(equalsCount==4)<br> { <br> myFile= SD.open(&quot;BlindCtl/Settings/Settings.txt&quot;,FILE_WRITE); <br> myFile.print(F(&quot;Hourx&quot;));<br> myFile.print(value); <br> myFile.print(&quot;z&quot;); <br> myFile.close();<br> }<br> if(equalsCount==5)<br> { <br> myFile= SD.open(&quot;BlindCtl/Settings/Settings.txt&quot;,FILE_WRITE); <br> myFile.print(F(&quot;Minx&quot;));<br> myFile.print(value); <br> myFile.print(&quot;z&quot;); <br> myFile.close();<br> }<br><br> equalsCount++;<br> value=&quot;&quot;; <br> }<br> }<br> storevalue=1; <br> } <br> // if you've gotten to the end of the line (received a newline<br> // character) and the line is blank, the http request has ended,<br> // so you can send a reply<br> if (c == '\n' &amp;&amp; currentLineIsBlank) <br> { <br> // send a standard http response header<br> client.println(F(&quot;HTTP/1.1 200 OK&quot;));<br> client.println(F(&quot;Content-Type: text/html&quot;));<br> client.println(); <br> //Any variables cannot contain the characters of z or x! Last variable values must end with xz.<br> client.println(F(&quot;&lt;div align='center'&quot;));<br> client.println(F(&quot;&lt;h1&gt;Windows Blinds Client V 1.0&lt;/h1&gt;&quot;));<br> client.println(F(&quot;&lt;/div&gt;&quot;));<br> // client.println(&quot;&lt;div align='center'&quot;);<br> if(timeOn==0) <br> { <br> client.println(F(&quot;&lt;form name='input' action='' method='get'&gt;&quot;));<br> client.println(F(&quot;&lt;input type='radio' name='action' value='Openxz' /&gt; Open&quot;));<br> client.println(F(&quot;&lt;input type='radio' name='action' value='Closexz' /&gt; Close&quot;));<br> client.println(F(&quot;&lt;input type='radio' name='action' value='Timexz' /&gt; Time&quot;)); <br> client.println(F(&quot;&lt;/br&gt;&quot;));<br> client.println(F(&quot;&lt;input type='submit' value='Submit'/&gt;&quot;));<br> client.println(F(&quot;&lt;/form&gt;&quot;));<br> }<br> else if(timeOn==1)<br> {<br> client.println(F(&quot;&lt;form name='input' action='' method='get'&gt;&quot;)); <br> client.println(F(&quot;Luminosity:&lt;input type='text' name='Hour' value='600'/&gt;&quot;));<br> client.println(F(&quot;Temp(close):&lt;input type='text' name='Minutes' value='26'/&gt;&quot;));<br> client.println(F(&quot;&lt;/br&gt;&quot;)); <br> client.println(F(&quot;Open Blinds At*(hour):&lt;input type='text' name='openTime'/&gt;&quot;));<br> client.println(F(&quot;Close Blinds At(hour):&lt;input type='text' name='closeTime'/&gt;&quot;));<br> client.println(F(&quot;&lt;/br&gt;&quot;));<br> client.println(F(&quot;Current Hour:&lt;input type='text' name='Hour'/&gt;&quot;));<br> client.println(F(&quot;Current Min:&lt;input type='text' name='Min'/&gt;&quot;));<br> client.println(F(&quot;&lt;input type=hidden name='action' value='xz' /&gt;&quot;));<br> client.println(F(&quot;&lt;input type='submit' value='Submit'/&gt;&quot;)); <br> client.println(F(&quot;&lt;/form&gt;&quot;));<br> client.println(F(&quot;&lt;/br&gt;&quot;)); <br> } <br><br> break;<br> }<br> if (c == '\n') {<br> // you're starting a new line<br> currentLineIsBlank = true;<br> } <br> else if (c != '\r') {<br> // you've gotten a character on the current line<br> currentLineIsBlank = false;<br> }<br> }<br><br><br> // if (loopcounter&lt;=readURL.length())<br> //loopcounter++; <br> }<br> // give the web browser time to receive the data<br> delay(1);<br> // close the connection:<br> client.stop();<br> SDread=1; <br> //Serial.print('Broswer is closed'); <br> }<br> return SDread; <br>}<br>void SDreader(const char* fileName)//function to read the values from teh SD card into variables<br>{<br> byte equalsCount=0;<br> String readURL; <br> //Serial.println('SD'); <br> myFile= SD.open(fileName); <br> while(myFile.available())//reads the entire file in a String<br> { <br> char c=myFile.read(); <br> readURL+=c; <br> }<br> myFile.close();<br> //Serial.println(readURL);<br> for(int i=0; i&lt;=(readURL.length()); i++)//extracts values from file that are between x and z<br> {<br><br> if(readURL[i]=='x')<br> { <br> int j=i+1;<br> i=j; <br> while(readURL[j]!='z')<br> {<br> value+=readURL[j];<br> j++;<br> } <br> //Serial.println(value);<br> if(equalsCount==0)<br> {<br> char string_as_char[value.length()+1];<br> value.toCharArray(string_as_char,value.length()+1); <br> Luminosity=atoi(string_as_char);<br> //Serial.println(Luminosity); <br> }<br> if(equalsCount==1)<br> {<br> char string_as_char[value.length()+1];<br> value.toCharArray(string_as_char,value.length()+1); <br> ctltemp=atoi(string_as_char);<br> //Serial.println(Luminosity); <br> }<br> if(equalsCount==2)<br> {<br> char string_as_char[value.length()+1];<br> value.toCharArray(string_as_char,value.length()+1); <br> openHour=atoi(string_as_char);<br> //Serial.println(Luminosity); <br> }<br><br> if(equalsCount==3)<br> {<br> char string_as_char[value.length()+1];<br> value.toCharArray(string_as_char,value.length()+1); <br> closeHour=atoi(string_as_char); <br> }<br><br> if(equalsCount==4)<br> {<br> char string_as_char[value.length()+1];<br> value.toCharArray(string_as_char,value.length()+1); <br> timeHour=atoi(string_as_char); <br> }<br> if(equalsCount==5){<br> char string_as_char[value.length()+1];<br> value.toCharArray(string_as_char,value.length()+1); <br> timeMin=atoi(string_as_char); <br> } <br><br> equalsCount++;<br> value=&quot;&quot;; <br> }<br> }<br> //Serial.print(Luminosity);<br> return; <br>}</p>
<p>The download link doesn't work. Please make it online again :)</p>
The download link does't work :(
<p>My problem is with making the blinds move with motors! specailly with my terrible blind that requires so much force to make move :) sounds like I've to get some decent blinds, maybe the one with motor pre installed!</p><p>is there anyway i can use a dc motor? </p>
Can I connect more than 1 motor ?
<p>There is now an automated blind retrofit kit on kickstarter. </p><p>https://www.kickstarter.com/projects/551966671/automate-your-blinds-with-a-smart-control-kit-in-1</p>
<p>If you're into doing this but the price tag is a tad steep you can look into getting a continuous servo motor instead, round $11 on eBay(just make sure it has enough torque). That way you don't need a motor shield. Also there are plenty of cheaper third party Ethernet shields on eBay (~$10), but supporting Arduino is nice of course :)</p>
<p>Really good</p>
<p>Its interesting</p>
<p>Thats brilliant</p>
<p><br><br>Thats extremely good</p>
<p><br>Its incredible :)</p>
<p><br>Very good</p>
<p><br>Its useful</p>
<p>Its striking<br><br></p>
<p>do you have the updated version of the code? because its not working it has alot of errors....any advised?</p>
do you have the updated version of the code? because its not working it has alot of errors....any advised?
do you have the updated version of the code? because its not working it has alot of errors....any advised?
Very nice job, and food for thought in the future. However, how the heck would one hook the servo to window blinds? Just wrapping the cord around the motor spindle won't do the job for very long. Using two power supplies makes it simple, but how many people have two outlets below the blind to plug in wall warts? One would have to use wall power since the servo would discharge a battery in a short time. I have to think of the aesthetics of a couple of wall warts plugged in below a window with the wires routing up the wall to a big ol' servo nailed somehow and attached to the cord or twist projection on the blind; especially since most wall warts are black.
Hey. If you connect the motor to a IKEA rollar blinds (come in various sizes and range from 10 to 20 dollars), the motors produces enough torque to rotate the shaft to open and close the blinds. As soon as I get time, I will upload a instructable on how to connect the motor to the blinds, but for now, experiment. As for the power required. A standard 9V power supply connected to the motor should last for a couple of weeks. I am working on a more efficient circuit design(the one given is extremely basic). Overall the conclusion is that this instructable is mostly an opportunity to experiment.
Hi, awesome tutorial, <br>did you get around on how to connect the motor to the blinds? <br>would be much appreciated, <br>thanks for your time. :)

About This Instructable




More by FreedomToCreate:Arduino controlled automated blinds with Web UI 
Add instructable to: