(not_so)Smart blinds

Hi All!

Yes, another beginner with another (hopefully easy to fix) problem… :slight_smile:

So what am I trying to do? Well, I want to have my window blinds set up to open in close depending on the temperature inside, and brightness outside. For example, if it’s cold inside, but sunny outside - open the blinds. But if it’s too hot inside already, even though it’s sunny, close the blinds.

Luckily for me, other people have wanted to do the same thing. So, I’ve managed to dissect parts of nicely written programs, and scramble them together to make a semi-functioning sketch. I say “semi-functioning” because now I want to add in an “override button”(everything else works as of now). Say, if I wanted to change my clothing during the day and wanted to close the blinds, or I wanted to open them at night to watch a meteor shower. This button is my stumbling block.

I want there to be only one button that basically moves my stepper motor the opposite of the current state of the blinds (eg. if blinds are open, closed them). I then want the program to wait until the same button is pressed a second time to engage the motor to step to the open position, before returning to the main loop to continue taking readings from the sensors.

Here’s what I have so far - I am fairly new at this, so if there’s anything that is obviously bad coding technique, I wouldn’t mind hearing about it too. Any help is greatly appreciated!

#include <AccelStepper.h>
AccelStepper motor1(1,3,4);     //step pin=3, dir pin=4

//start defines//
  
  #define LIGHT_THRESHOLD 650   //This is where you set the ligth intensity to set the blinds im motion
  #define TEMP_THRESHOLD   24   //This is where you set the temperature to set the blinds in motion 
  #define LIGHT_PIN         0   //set LDR as pin A0    
  #define TEMP_PIN          5   //set tmp36 thermometer as A5 
  #define ONBOARD_LED      13 
  #define TOGGLE_BUTTON     7
//end defines//

//variables//
int pos = 500000;                   //This is where you set the number of steps your stepper must move to reach the open position
int enable_pin        = 8;         //pin that toggles power on and off to driver board
int curtain_state     = 1; 
int light_status      = 0;
const int button_pin  = 7;
int buttonState       = 0;
int lastButtonState   = 0;
int buttonPresses = 0;
int main_button_state;
boolean daylight   = true;
boolean warm       = false;
//smoothing variables
const int numReadings = 10;     //change this to adjust the number of values in your sample you want to average
int readings[numReadings];      // the readings from the analog input
int index = 0;                  // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average
//end variables//

/////////////////////////////////////////////////////////////////////////////////////////////

//start curtain subroutine - this is the main subroutine that controls the movement of the curtains
void Curtain(boolean curtain_state) {   
  digitalWrite(ONBOARD_LED, curtain_state ? HIGH : LOW);
  if (curtain_state) {
    digitalWrite(enable_pin, LOW);
    Serial.println("Opening curtain...");
        motor1.move(pos);
        while (motor1.distanceToGo()!=0)
        motor1.run();
        digitalWrite(enable_pin, HIGH);        
      }else{
         digitalWrite(enable_pin, LOW);
         Serial.println("Closing curtain...");
         motor1.move(-pos);
         while (motor1.distanceToGo()!=0)
         motor1.run();
         digitalWrite(enable_pin, HIGH);
       }
}

//end curtain subroutine//

//start button_press subroutine//

void button_press_darken(){
   buttonState=digitalRead(TOGGLE_BUTTON);
       if (buttonState != lastButtonState){
      if (buttonState ==HIGH){
         digitalWrite(enable_pin, LOW);
         Serial.println("Manual Open");
         motor1.move(-pos);
         while (motor1.distanceToGo()!=0)
         motor1.run();
         digitalWrite(enable_pin, HIGH);
       }
       Serial.println("Open");
       }

         lastButtonState = buttonState; 
 

      if (buttonState==LOW){
        digitalWrite(enable_pin, LOW);
         Serial.println("Manual Close");
         motor1.move(pos);
         while (motor1.distanceToGo()!=0)
         motor1.run();
         digitalWrite(enable_pin, HIGH);
          lastButtonState = buttonState; 
    }      
}

void button_press_lighten(){
    buttonState=digitalRead(TOGGLE_BUTTON);
       if (buttonState != lastButtonState){
      if (buttonState ==HIGH){
         digitalWrite(enable_pin, LOW);
         Serial.println("Manual Close");
         motor1.move(-pos);
         while (motor1.distanceToGo()!=0)
         motor1.run();
         digitalWrite(enable_pin, HIGH);
       }
       Serial.println("Closed");
       }

         lastButtonState = buttonState; 
 

      if (buttonState==LOW){
        digitalWrite(enable_pin, LOW);
         Serial.println("Manual Open");
         motor1.move(pos);
         while (motor1.distanceToGo()!=0)
         motor1.run();
         digitalWrite(enable_pin, HIGH);
          lastButtonState = buttonState; 
      }

  }    
//end button subroutine//

//START:setup//
void setup() { 
  for (int thisReading = 0; thisReading < numReadings; thisReading++)   //this is for smoothing temp
  readings[thisReading] = 0;                                                                                      //this is for smoothing temp  
  pinMode (button_pin, INPUT);
  //pinMode (TOGGLE_BUTTON, INPUT);         //PIN 7 defined as an input
  pinMode (enable_pin, OUTPUT);            //defines enable_pin as an output   
  analogReference(INTERNAL);               //sets vref to internal 1.1v
  motor1.setMaxSpeed(10000);              //set top limit speed of motor
  motor1.setSpeed(9500);                  //set max speed you want it to run 
  motor1.setAcceleration(2000);           //set acceleration of motor
  Serial.begin(9600);
  Serial.println("Setting up Curtain Automation...");
}
//END:setup//

//START:main_loop//
void loop(){ 
  
  //temp smoothing loop
  total= total - readings[index];          //subtract the last reading
  readings[index] = analogRead(TEMP_PIN);  //read the sensor
  total= total + readings[index];          //add the reading to the total
  index = index + 1;                       //advance to nex position in array
  if (index >= numReadings)                //if at end if array..
  index = 0;                               //...wrap it
  average = total / numReadings;           //calculate average       
  //temp smoothing loop
  
  digitalWrite(enable_pin, HIGH);          //turns power off to driverboard to conserve power 
  light_status = analogRead(LIGHT_PIN);    //check LDR and print to Serial Monitor
  delay(2000);
          Serial.print("Photocell value = ");
          Serial.println(light_status);
          Serial.println("");

  float temp_Celsius =  float(average) *110/1024;  //check thermometer and print to Serial Monitor
  float temp_Fahrenheit = (temp_Celsius * 9 / 5) + 32;
          Serial.print("Temperature value (Celsius) = ");
          Serial.println(temp_Celsius,1);
          Serial.print("Temperature value (Fahrenheit) = ");
          Serial.println(temp_Fahrenheit,1);
          Serial.println("");
  
  if (light_status > LIGHT_THRESHOLD)
      daylight = true;
  else
      daylight = false; 
    
  if (temp_Celsius > TEMP_THRESHOLD)
      warm = true;
  else
      warm = false;

  switch (curtain_state)
  {
  case 0:
      if (daylight && !warm)
      // open curtain
      {     
        curtain_state = 1;
        Curtain(curtain_state);
        
    }
       main_button_state =digitalRead(TOGGLE_BUTTON);
    if (main_button_state ==HIGH){
      button_press_darken();
    }   
      break;

  case 1:
      if (!daylight || warm)
      // close curtain
      {
        curtain_state = 0;
        Curtain(curtain_state);
    }
     main_button_state =digitalRead(TOGGLE_BUTTON);
    if (main_button_state ==HIGH){
      button_press_lighten();
    }   
      break;
  }
  
}
//END:main_loop
int pos = 500000;                   //This is where you set the number of steps your stepper must move to reach the open position

That won't fit into an int (maximum 32767).

Thanks Nick, Whoops!! I've changed the int to long. Any suggestions on the button problem?

OK, well I think you need to detect a transition for a start.

http://www.gammon.com.au/switches

Once you have a transition (button now pressed but it wasn’t before) you toggle some sort of flag, the “do the opposite flag”.

Then when you go to change the curtain state you test that flag, and if set do the opposite of what you were planning to do.

eg. in pseudo-code:

if <need to raise curtain>
  if <do the opposite flag set>
    (lower the curtain)
  else
    (raise the curtain)
  delay(2000);

Be warned that this delay will mean you will have to hold the button for at least two seconds, or it might not notice you pressing it. Maybe get rid of that and restructure.

http://www.gammon.com.au/blink

Using Tools + Auto Format would make that mess more readable. The body of each of your while statements is indented the same amount as the while statement, which is wrong.

Your function names leave a lot to be desired. One can look at a statement like motor1.run(), and figure out right away what the function does. One can not look at a Curtain() or button_press_darken() or button_press_lighten() and know what they do.

Your loop() function is way too long, and is doing way to much stuff. Some functions, with meaningful names, like getTemperature() and getLightLevel() and isDaylight() and isWarm() would go a long way towards producing better code.

It is easy to test a function like isDaylight(). Once you know it works, then some code like:

if(isDaylight())
{
   // open the blinds
}

is nearly instantly understandable.

if(isDaylight() && !isWarm())
{
}

also is very clear what the intent is.

  for (int thisReading = 0; thisReading < numReadings; thisReading++)   //this is for smoothing temp
  readings[thisReading] = 0;

Index names are generally short - typically one letter. There is no reason to use a name like thisReading as an index. The name IMPLIES that it is a reading, not an index.

This loop isn’t even necessary. Since the array is global, the compiler already generated code to zero out all the elements.

Nick:

Thanks for the pseudo-code and comments about the delay. I added the delay so I could read the temp results more easily - though I do, like you say, have to hold down the button for a few seconds for it to register. I’ll take it out once the sketch is running smoothly. I’ll see what I can come up with after your flag suggestions - it’s interesting to me that I basically have to think backwards to get the results I want. Makes my head spin a little. Just though I’d ask - you wouldn’t happen to have an example that you could point me towards, do you? :slight_smile:

PaulS:

Holy! I didn’t even know there was an Auto Format option! Thanks for the suggestion - things look much better now. About the function names - I’ll admit that several of my function names are vague. To be honest, I was never expecting anyone else to ever see this code. But extremely valid point nonetheless. I’ll see what I can come up with and replace them with more definitive names.

Is there a reason why the main loop() function should be kept short, or is just for clarity’s sake? After all, If I’m calling up other functions in the loop that run the same code as I have in there now, that’s the only difference I can see. Am I right?

  for (int thisReading = 0; thisReading < numReadings; thisReading++)   //this is for smoothing temp
  readings[thisReading] = 0;

One of the many downsides of being a beginner and hashing sketches together is that I don’t know what some parts of other peoples code actually do. Most times I can figure it out, but other times I have to throw in the towel and assume that the original author of the code knew what they were doing. The code above - case in point. *I * even though that it was taking a reading from somewhere. But over the week, I’ve seen enough code to recognize the format, and yes, like you say, I’ve noticed index names are usually one letter. Thanks for bringing it to my attention and letting me know it’s a useless part of the code - I’ve taken it out, and the temperature readings still work great!

Thanks for the all very helpful comments!
Cheers!

Just though I’d ask - you wouldn’t happen to have an example that you could point me towards, do you?

Not really, as I haven’t had that particular problem. But think how rewarding it will be to get it working yourself. :slight_smile:

Follow the pseudo code. Or even think “how would I do it if I was inside the processor?”

'll see what I can come up with and replace them with more definitive names.

It's not just for our benefit. You will benefit greatly, too. It is not hard to come up with good names. The function does something. The name should be obvious, based on what the function does. The problem arises when the function name needs to be doThisAndThatAndSomethingElseTooOhAndDontForgetThatWeAlsoDoTheOhterThing() to describe what the function does.

After a while, picking good names becomes second nature, and you'll look at early code with names like PerformTask() and wonder what the hell you were thinking.

Is there a reason why the main loop() function should be kept short, or is just for clarity's sake?

Isn't that a sufficient reason? I teach that functions should be able to be printed on one page. More than that, and by the time you read to the end, you've forgotten what happened at the top. Or, maybe that's old age. But, loop() is a function, like any other function. It should do one thing, and do that one thing very well. Primarily, that one thing is the whole application, but the real work is done in functions. Functions should be specialized, like contractors. The loop function should be like the general contractor, in charge of hiring the subcontractors, getting them to do their part on time, etc. The general contractor shouldn't be poring the foundation, doing the wiring, running the plumbing, putting the roof on, etc. The various subcontractors should be doing the work, while the general contractor pays them next to nothing.

After all, If I'm calling up other functions in the loop that run the same code as I have in there now, that's the only difference I can see. Am I right?

Yes, but it's easier to test a function that does one thing, and does that one thing well. Like the contractor analogy, if a function does roofing, it doesn't need to do plumbing. You can verify that the roofer does roofing well, and then just call the roofer when you need a roof. You don't need to be concerned with how well the roofer does plumbing.

With all the code in loop(),. it's like the general contractor trying to do all the work, and manage the process at the same time.

One of the many downsides of being a beginner and hashing sketches together is that I don't know what some parts of other peoples code actually do.

A difficult spot to be in, for sure. But, that's where meaningful function and variable names some in handy. And, small functions that are easy to understand.

Most times I can figure it out, but other times I have to throw in the towel and assume that the original author of the code knew what they were doing.

I don't like assuming things. That's why I stress organization, meaningful names, short functions, etc. Short functions with good names and proper comments are easy to understand.

These comments are not all addressed specifically to you. They apply to a lot of new programmers. You seem willing to listen, though. I think you'll do quite well.

The blink without delay example and the state change detection examples should become your go-to sketches for borrowing code.

Well explained, PaulS.

I’ve added your comments to my tips and traps page (tip 10) as that is good advice for everyone.

http://www.gammon.com.au/tips#tip10

Most times I can figure it out, but other times I have to throw in the towel and assume that the original author of the code knew what they were doing.

Not necessarily .

Not necessarily .

Just look at all the instructables stuff people drag in here…

Well explained, PaulS.

Thanks.

Hi again!

Here’s my new and improved sketch:

/*Sketch to control Smartblinds.  User sets thresholds for temp and brightness, and how many steps to take to close the curtains.
 I have very little programming experience, so this is a mashup of several differnet sketches.  Cheers! */

#include <AccelStepper.h>
AccelStepper motor1(1,3,4);     //step pin=3, dir pin=4


#define LIGHT_THRESHOLD   650       //This is where you set the ligth intensity threshold to set the blinds im motion
#define TEMP_THRESHOLD     24       //This is where you set the temperature threshold to set the blinds in motion 
#define LIGHT_PIN           0       //set LDR as pin A0    
#define TEMP_PIN            5       //set tmp36 thermometer as A5 
#define ONBOARD_LED        13 
#define TOGGLE_BUTTON       7

long pos                  = 1500;   //This is where you set the number of steps your stepper must move to reach the open position
int enable_pin            = 8;      //pin that toggles power on and off to driver board
int curtain_buttonstate   = 1; 
int light_status          = 0;
int buttonval             = 0;
int oldbuttonval          = 0;
int buttonstate           = 0;
boolean daylight          = true;
boolean warm              = false;

//temperature smoothing variables
const int temperaturenumReadings = 10;                 //change this to adjust the number of values in your sample you want to temperature_average
int temperature_readings[temperaturenumReadings];      // the temperature_readings from the analog input
int temperature_index = 0;                             // the temperature_index of the current reading
int temperature_total = 0;                             // the running temperature_total
int temperature_average = 0;                           // the temperature_average



void sensor_dependent_movement(boolean curtain_buttonstate) {   
  digitalWrite(ONBOARD_LED, curtain_buttonstate ? HIGH : LOW);
  if (curtain_buttonstate) {
    digitalWrite(enable_pin, LOW);
    Serial.println("Sensor opening curtain...");
    motor1.move(pos);
    while (motor1.distanceToGo()!=0)
      motor1.run();
    digitalWrite(enable_pin, HIGH);        
  }
  else{
    digitalWrite(enable_pin, LOW);
    Serial.println("Sensor closing curtain...");
    motor1.move(-pos);
    while (motor1.distanceToGo()!=0)
      motor1.run();
    digitalWrite(enable_pin, HIGH);
  }
}



void button_override_lighten(){

  buttonval=digitalRead(TOGGLE_BUTTON);
  if ((buttonval==HIGH) && (oldbuttonval==HIGH)){
    buttonstate=1-buttonstate;
    delay (200);
  }
  oldbuttonval=buttonval;
  if (buttonstate==1){
    digitalWrite(enable_pin, LOW);
    Serial.println("Manual Close");
    motor1.move(-pos);
    while (motor1.distanceToGo()!=0)
      motor1.run();

  }
  else{
    digitalWrite(enable_pin, LOW);
    Serial.println("Manual Open");
    motor1.move(pos);
    while (motor1.distanceToGo()!=0)
      motor1.run();
  }
}


void button_override_darken(){

  buttonval=digitalRead(TOGGLE_BUTTON);
  if ((buttonval==HIGH) && (oldbuttonval==HIGH)){
    buttonstate=1-buttonstate;
    delay (200);
  }
  oldbuttonval=buttonval;
  if (buttonstate==1){
    digitalWrite(enable_pin, LOW);
    Serial.println("Manual Open");
    motor1.move(pos);
    while (motor1.distanceToGo()!=0)
      motor1.run();

  }
  else{
    digitalWrite(enable_pin, LOW);
    Serial.println("Manual Close");
    motor1.move(-pos);
    while (motor1.distanceToGo()!=0)
      motor1.run();
  }
}


void(temperature)(){

  //this part is for smoothing temp readings
  temperature_total= temperature_total - temperature_readings[temperature_index];            //subtract the last reading
  temperature_readings[temperature_index] = analogRead(TEMP_PIN);                            //read the sensor
  temperature_total= temperature_total + temperature_readings[temperature_index];            //add the reading to the temperature_total
  temperature_index = temperature_index + 1;                                                 //advance to nex position in array
  if (temperature_index >= temperaturenumReadings)                                           //if at end if array..
    temperature_index = 0;                                                                   //...wrap it
  temperature_average = temperature_total / temperaturenumReadings;                          //calculate temperature_average       

  float temp_Celsius =  float(temperature_average) *110/1024;                                //check thermometer and print to Serial Monitor
  float temp_Fahrenheit = (temp_Celsius * 9 / 5) + 32;
  Serial.print("Temperature value (Celsius) = ");
  Serial.println(temp_Celsius,1);
  Serial.print("Temperature value (Fahrenheit) = ");
  Serial.println(temp_Fahrenheit,1);
  Serial.println("");
  if (temp_Celsius > TEMP_THRESHOLD)
    warm = true;
  else
    warm = false;
}


void(light)(){
  light_status = analogRead(LIGHT_PIN);      //check LDR and print to Serial Monitor
  Serial.print("Photocell value = ");
  Serial.println(light_status);
  Serial.println("");
  if (light_status > LIGHT_THRESHOLD)
    daylight = true;
  else
    daylight = false; 
}


void(switchstate)(){

  switch (curtain_buttonstate)
  {
  case 0:
    if (daylight && !warm)
      // open curtain
    {     
      curtain_buttonstate = 1;
      sensor_dependent_movement(curtain_buttonstate);
    }
    buttonval =digitalRead(TOGGLE_BUTTON);
    if (buttonval ==HIGH){
      button_override_lighten();
    }   
    break;

  case 1:
    if (!daylight || warm)
      // close curtain
    {
      curtain_buttonstate = 0;
      sensor_dependent_movement(curtain_buttonstate);
    }
    buttonval =digitalRead(TOGGLE_BUTTON);
    if (buttonval ==HIGH){
      button_override_darken();
    }   
    break;
  }
}



void setup() { 


  pinMode (TOGGLE_BUTTON, INPUT);            //PIN 7 defined as an input
  pinMode (enable_pin, OUTPUT);              //defines enable_pin as an output   
  analogReference(INTERNAL);                 //sets vref to internal 1.1v
  motor1.setMaxSpeed(10000);                 //set top limit speed of motor
  motor1.setSpeed(9500);                     //set max speed you want it to run 
  motor1.setAcceleration(2000);              //set acceleration of motor
  Serial.begin(9600);
  Serial.println("Setting up Curtain Automation...");
}



void loop(){ 

  digitalWrite(enable_pin, HIGH);            //turns power off to driverboard to conserve power 
  temperature();
  light();
  switchstate();
}

I wanted to move the setup() and loop() to the top of the sketch so anyone else could easily see it without scrolling down to the bottom, but it apparently it prefers to be at the bottom below the rest of the functions. Also, I wanted to nest the sensor_dependent_movement() and both button_override functions in the switchstate() function, but I kept on getting errors, so decided to leave them out.

PaulS :
Thanks for the tips - I especially like the contractor analogy - I’m in the construction industry, so having you describe the main loop in those words really made sense to me! I’d appreciate anymore feedback on the current version too.

Although I did spend about 10 frustrating hours trying to troubleshoot it, my override button does work now. But now I’m finding myself with another problem after I neglected to considered a situation that is fairly obvious now that my sketch is running with my motor. This is the problem:

If I override my program with a button push and don’t return it to the correct current state determined by the loop, and the weather outside changes, my motor will want to move in the wrong direction. This is not a good thing. :slight_smile: As long as I always return it, there’s no problem. But say if I were to override, then leave the house for a few days, something’s - either the motor of the blinds - is going to break.

Any suggestions on where to look at to correct this? I was really impressed with the thought process that Nick suggested in the pseudo-code - it was really extremely helpful and would mind more feedback regarding this problem. Thanks for your help up to this point - I really would have been lost without it!
Cheers!

Well, you said you wanted to override normal function to change clothes or watch a meteor shower. So I am presuming the override is for short intervals.

You could have a test for time. Save in a variable when you detected an override button press. After a certain time (say, an hour) simply cancel the override toggle.

Paul1989: This is the problem:

If I override my program with a button push and don't return it to the correct current state determined by the loop, and the weather outside changes, my motor will want to move in the wrong direction. This is not a good thing. :) As long as I always return it, there's no problem. But say if I were to override, then leave the house for a few days, something's - either the motor of the blinds - is going to break.

Any suggestions on where to look at to correct this? I was really impressed with the thought process that Nick suggested in the pseudo-code - it was really extremely helpful and would mind more feedback regarding this problem. Thanks for your help up to this point - I really would have been lost without it! Cheers!

You could set a variable (call it a flag) that your motor code would check to see if it should move or not, until the override is reset.

You could also put limit switches at the fully or next-to fully opened and closed positions and then check the switch for the direction you want to turn before each step (the step takes way longer than the switch check, the added time won't be noticeable) to protect your hardware.

Nick:

Yes, I was planning to used it for short intervals to override the program, but, as a forgetful fellow sometimes, I thought it would be prudent to plan for a lapse of memory every now and then. I too was actually thinking about using some kind function that returns it automatically after some amount of time, but then realized that the solution isn't that easy. I was looking at various pages describing power saving options (including your very well written page ),since I eventually want this project to be solar powered. I was thinking of using a Watchdog Timer to power down the Arduino to save power, and wake it every 8 seconds or so. The "blind open" default state is set to 1, so if the sun goes down while it's asleep, with a watchdog timer waking it up every 8 seconds, the LDR will register light levers below the open state threshold, and will attempt to close the blinds on each wake up. I'm still not even sure if I will implement the watchdog timer, because I think that the easydriver board needs a high signal on it's enable pin, and if the arduino is off, it can't supply the signal, thus leaving the driver board on and wasting power.....hmmmm, I'll have to look into that.

GoforSmoke:

You're right, limit switches are probably the safest way to go to ensure my blinds/motor doesn't get trashed , but I'd rather not use them. Aside from the additional cost and more complicated install, I want to have as few parts of the project physically visible, and with a lower limit switch, I'm not sure how well I could hide it...I'm sure I could, but I was really hoping for a programming solution. In the end, I might have to go in this direction though.

With tin foil and tape and a resistor or two you could make a capacitive sensor. With wire and a bit of conductive foam you could make a pressure sensor. With wires alone you can make a contact sensor. Yah, real expensive. Very hard to hide. You can put them on the motor itself but that would give it away.

So, like I previously mentioned, I eventually want to make this project solar powered, and following in that vein, am looking at power saving options. One of the options I came across was the Watchdog Timer (WDT), which basically puts the microprocessor to sleep for periods of up to eight seconds. This process can be repeated as many times as you want, so if I wanted to sleep for roughly a minute (56 secs.) you’d run the watchdog timer 7 times. I had assumed that it would shut down all power supplied to the Arduino - pins, leds, etc. but apparently it only effects the microprocessor. Ok then. Small steps. Eventually, I might try using a bare bones type of setup like the one described on Nick’s page (Gammon Forum : Electronics : Microprocessors : Power saving techniques for microprocessors). One of the things I like about the Arduino platform is that it seems like it’s somewhat idiot-proofed, which is great for me. The bare bones version looks like it could be screwed up much more easily. But I digress…

I added the WDT to my program today, and wasn’t too surprised to find that it did exactly what I didn’t want it to do with my code: when it woke from the timer, it checked the LDR, and if was below the threshold, it would close - every eight seconds. Of course, you can’t close a closed blind, so I’m in a little bit of a pickle. So, now accepting suggestions on how I might go about fixing this one. Or, perhaps there is someone out there that may know something about solar power that could tell me whether or not even using a WDT makes sense if I’m not going to be switching from my UNO to something more energy efficient.

If I am using the WDT, I’m also thinking I’m going to have to attach an interrupt to my override button, to wake the WDT and run my override functions. After successfully going the interrupt code examples today, I tried to implement it into my code, but for some reason, it’s not working. Is it not feasible to override the WDT with an external interrupt?

This page describes powering off peripherals:

http://www.gammon.com.au/forum/?id=12106

If I am using the WDT, I'm also thinking I'm going to have to attach an interrupt to my override button, to wake the WDT and run my override functions. After successfully going the interrupt code examples today, I tried to implement it into my code, but for some reason, it's not working. Is it not feasible to override the WDT with an external interrupt?

Yes, you can do both, as my humidity sensor does.

... when it woke from the timer, it checked the LDR, and if was below the threshold, it would close - every eight seconds.

You are going to have to restructure aren't you? Whether or not it was asleep you don't want to close blinds that are closed.

You can run a stand-alone AVR at low clock speed to save power and run at lower voltage. You don't need 16 MHz to deal with blinds. An 8 to 14 pin ATtiny will probably do and there are files for the Tiny45 and Tiny85 available. The most energy you need will be to turn the motor but if you do that slowly then even that won't take much.

MIT High-Low Tech -- Programming an ATtiny w/ Arduino 1.0 http://hlt.media.mit.edu/?p=1695

ATtiny45V and 85V run active at 1 MHz on 1.8V, 300 uA.

The early Apple, Commodore and Atari computers ran at 1 Mhz. That's faster than you need.

Thanks for the replies!

Nick, thanks for the link on the peripherals , I'll check it out as soon as I can. I guess for now though I should focus on the modification of the sketch to work out the close-close problem. I might be awhile! :)

GoForSmoke, you've opened my eyes! I've heard of the ATtiny's here and there but I never knew what they really were. I spent a good deal of time last night checking them out online and looking at projects that use them - and so cheap too! So thanks for the heads up, and for the links. Do you have any experience with Digispark or Adafruits Trinket? They seem like they would fit my requirements, especially the 3.3v Trinket, even though it's almost 7 times more expensive than the bare chip. Thoughts?