Problem with My Engineering Project

Another way to look at it is that you read the sensor and use it to determine whether the blinds should be open or closed.

As Blackfin suggests, you must keep track of whether the blinds actually are open or closed.

If they're not in the desired state, change and record it. Otherwise, do nothing.

No, I haven't heard that term. Hysteresis? Again, I'm new to all this. (Second year electrical engineering student). Pretty much a newbie if I'm being honest.

Do you have a means to determine if the blinds are completely closed or completely open?

For example, a limit switch on both extremes? What happens if the motor continues to attempt to close or open the blinds and they're at one extreme or the other?

re hysteresis: Do you know about Schmitt-trigger logic devices? The idea is that the turn-on and turn-off points of a circuit are not the same. Having a "dead band" between the two prevents chatter, noise and control problems.

Another issue that is going to bite you sooner or later is the use of delay. This will prevent you from extending your sketch without breaking what you already have.

Have a look at the following example

File -> Examples -> 02.Digital -> BlinkWithoutDelay

The goal should be to have loop runs as often as possible. Divide you sketch into tasks e.g. reading sensors, sending data, controlling a motor ... The tasks should not block other code. They should check if they need to do something and then move on. This way you get multi tasking where things seem to happen all at the same time and you can add more functionality as your project develops.

A few tiny enhancements I would recommend

  • use #define for constants and use the commonly use naming convention CAPITAL_WITH_UNDERSCORE e.g.

#define LIGHT_THRESHOLD_HIGH 700
#define LIGHT_THRESHOLD_LOW 650

I even use it for pins because the value should not change. So, there is no need for a variable.

#define MOTOR_PIN 3

  • Chose good variable names

lightLevel is better than state, I would reserve state for bool and state machine states

controlPin1 and 2 is not very specific, what do they do?

lastLS and lightState could be oldLightLevel and newLightLevel, current and previous are often used as well, this will make it more clear that they belong together

Here's something that compiles but isn't tested. It attempts to maintain a target brightness using states and hysteresis. I've chosen a smaller value for the motor speed and an arbitrary value for the brightness. You'll need to play with those.

You might consider adding a serial interface feature to allow you to select a brightness level.

Edit: Removed unnecessary/unused code.

Extending beyond the idea of opening the blinds whenever it's dark inside, what should happen if it's darker outside than inside? Blinds regulate the interior lighting but they also provide privacy (unless these are classroom blinds)

A friend of mine was walking around naked in his new home with tinted sliding glass doors because he did not realize that at night when his lights are on, the tint no longer blocks people from seeing in. 'Howdy new neighbors, it's SHOWTIME!' His curtains are now closed when exterior light is lower than interior light.

What if it's just a cloud passing by the sun? Should the blinds flap open or closed for transient changes? If the motor is slow enough, that's a moot point but otherwise it might be worthwhile to average lighting measurements over time in addition to hysteresis.

If everything is optimized for moving really slowly, it might help battery life to sleep.

With a little more thought, it could get even more ridiculously complicated. Status of light switch? Override for dark preference? Clock/calendar settings? Motion sensors for unoccupied rooms? Network control and reporting? Solar battery charging? (is this an actual project or just a lab exercise to demonstrate how to control a motor with a light sensor?)

Blackfin:
Here's something that compiles but isn't tested. It attempts to maintain a target brightness using states and hysteresis. I've chosen a smaller value for the motor speed and an arbitrary value for the brightness. You'll need to play with those.

You might consider adding a serial interface feature to allow you to select a brightness level.

The code assumes (maybe incorrectly) you have limit switches. These would be important, for example, at night time: no matter how much you keep opening the blinds you'll never reach the desired brightness. But you don't really want to run the motor indefinitely trying. So a limit switch tells the Arduino the blinds are as open as they'll get and to just stop trying (and possibly damaging the motor or blind mechanism in the process.)

If you don't have limit switches you can comment out or remove those checks. I strongly recommend some form of limiting though...

//pins

const uint8_t controlPin1 = 5;
const uint8_t controlPin2 = 3;
const uint8_t enablePin = 9;
const uint8_t lightPin = A0;

const uint8_t limitswitchOpen = 6;
const uint8_t limitswitchClose = 7;

//constants
const uint8_t motorSpeed = 40;          //slow speed for accurate brightness control

uint16_t lightState = 0;

#define K_TARGET_BRIGHTNESS     550
#define K_HYST_LOW              25
#define K_HYST_HIGH             25
#define K_MOVE_TIME             1000ul
#define K_SENSOR_READ_TIME      50ul

#define K_LIMSW_ACTIVE_LVL      LOW
#define K_LIMSW_INACTIVE_LVL    HIGH

enum statesBlinds
{
   ST_BLINDS_IDLE=0,
   ST_BLINDS_CLOSING,
   ST_BLINDS_OPENING    
       
};

void setup( void )
{
   Serial.begin(9600);
   
   pinMode( lightPin, INPUT );
   pinMode( controlPin1, OUTPUT );
   pinMode( controlPin2, OUTPUT );
   pinMode( enablePin, OUTPUT );
   digitalWrite( enablePin, LOW );

pinMode( limitswitchOpen, INPUT_PULLUP );
   pinMode( limitswitchClose, INPUT_PULLUP );

}//setup

void loop( void )
{
   static uint8_t
       stateBlinds = ST_BLINDS_IDLE,
       nextState;
   static uint32_t
       timeSensor,
       timeBlinds;
       
   uint32_t timeNow = millis();

//read the sensor every K_SENSOR_READ_TIME milliseconds
   if( (timeNow - timeSensor) >= K_SENSOR_READ_TIME )
   {
       timeSensor = timeNow;
       lightState = analogRead(lightPin);
       Serial.println(lightState);

}//if
         
   switch( stateBlinds )
   {
       case    ST_BLINDS_IDLE:
           if( lightState >= (K_TARGET_BRIGHTNESS + K_HYST_HIGH) )
           {
               //Q: is brightness level too high?
               //yes; start blinds moving in the closing direction
               if( digitalRead( limitswitchClose ) == K_LIMSW_INACTIVE_LVL )    //move only if limit-switch allows it
               {
                   digitalWrite( enablePin, HIGH );
                   analogWrite( controlPin1, motorSpeed );
                   digitalWrite( controlPin2, LOW );
                   
                   stateBlinds = ST_BLINDS_CLOSING;                
                   
               }//if
                               
           }//if
           else if( lightState <= (K_TARGET_BRIGHTNESS - K_HYST_LOW) )
           {
               //Q: is brightness level too low?
               //yes; start blinds moving in the opening direction
               if( digitalRead( limitswitchOpen ) == K_LIMSW_INACTIVE_LVL )    //move only if limit-switch allows it
               {
                   digitalWrite( enablePin, HIGH );
                   analogWrite( controlPin1, LOW );                
                   digitalWrite( controlPin2, motorSpeed );
                                   
                   stateBlinds = ST_BLINDS_OPENING;                
                   
               }//if
               
           }//else
           
       break;

case    ST_BLINDS_CLOSING:            
           //if limit switch closes or we reach the target brightness...
           if( (digitalRead( limitswitchClose ) == K_LIMSW_ACTIVE_LVL ) || (lightState <= K_TARGET_BRIGHTNESS) )
           {
               //stop motor and...
               digitalWrite( enablePin, LOW );
               digitalWrite( controlPin1, LOW );
               digitalWrite( controlPin2, LOW );

//return to idle state
               stateBlinds = ST_BLINDS_IDLE;
               
           }//if
           
       break;
       
       case    ST_BLINDS_OPENING:
           //similar logic to closing direction
           if( (digitalRead( limitswitchOpen ) == K_LIMSW_ACTIVE_LVL ) || (lightState >= K_TARGET_BRIGHTNESS) )
           {
               digitalWrite( enablePin, LOW );
               digitalWrite( controlPin1, LOW );
               digitalWrite( controlPin2, LOW );

stateBlinds = ST_BLINDS_IDLE;
               
           }//if
           
       break;
               
   }//switch
   
}//loop

I want to say thank you for taking the time to write all that out for me and prove me with an example of the code you think is best for my circuit.
No I did not incorporate limit switches. My reasoning is this: instead of using a limit switch to prevent turning I wanted to calibrate the time the motor turns precisely by using some math to convert the rotations of the motor into distance of string being moved in the open/close direction. Does that make sense? If, in order to open the blinds, the motor needs to move x inches, well... I've calibrated the motor to turn at a speed I determine, right? And if I can also determine the amount of time it is functioning, well then its just a simple formula of velocity x time = distance. So I just need to tell the motor to turn for x seconds. That would be something I would fine-tune later. And my intent was to use the delay function to time the motor.
Like so:
digitalWrite(enablePin, HIGH);
delay(500); (500 being an arbitrary number)
And it shouldn't matter too much if I'm delaying all other operations because while the blinds are opening/closing I don't really need to worry about what the light sensor reading is or anything else for that matter. After the blinds open/close is a different story. But while the motor is functioning, that's all I need my device to do.
At least that was my reasoning. Again, I'm new to all of this. Perhaps I'm being naive or unaware. But I was trying to make the simplest possible circuit for my very first engineering class. But again, thank you very much for writing all that out, although I don't have limit switches in my circuit. Bear in mind that at this particular moment, I'm not trying to develop an actual device that will really go on a set of blinds and start working. At this point I'm just trying to develop a prototype so to speak, something I will fine-tune later in this process. A working foundation. I wish I had more time to learn more about the concepts of coding, but time is limited.

Hystersis should be incorporated into your project, as suggested above. But it won't help with the situation when light levels are constantly increasing, such as when the sun is rising. You will still get continuous adjustments of the blinds. (Given a low servo speed, is that really a bad thing?)

Have you considered dividing the state of the blinds into a few chunks, for example: 0%, 25%, 50%, 75% and 100% open. Your servo system would only be activated when the ambient light level would translate to a chunk different than the current setting, otherwise nothing would happen. You'd have lots of freedom to choose the "granularity" of the setting.

S.

edmcguirk:
Extending beyond the idea of opening the blinds whenever it's dark inside, what should happen if it's darker outside than inside? Blinds regulate the interior lighting but they also provide privacy (unless these are classroom blinds)

A friend of mine was walking around naked in his new home with tinted sliding glass doors because he did not realize that at night when his lights are on, the tint no longer blocks people from seeing in. 'Howdy new neighbors, it's SHOWTIME!' His curtains are now closed when exterior light is lower than interior light.

What if it's just a cloud passing by the sun? Should the blinds flap open or closed for transient changes? If the motor is slow enough, that's a moot point but otherwise it might be worthwhile to average lighting measurements over time in addition to hysteresis.

If everything is optimized for moving really slowly, it might help battery life to sleep.

With a little more thought, it could get even more ridiculously complicated. Status of light switch? Override for dark preference? Clock/calendar settings? Motion sensors for unoccupied rooms? Network control and reporting? Solar battery charging? (is this an actual project or just a lab exercise to demonstrate how to control a motor with a light sensor?)

edmcguirk:
Extending beyond the idea of opening the blinds whenever it's dark inside, what should happen if it's darker outside than inside? Blinds regulate the interior lighting but they also provide privacy (unless these are classroom blinds)

A friend of mine was walking around naked in his new home with tinted sliding glass doors because he did not realize that at night when his lights are on, the tint no longer blocks people from seeing in. 'Howdy new neighbors, it's SHOWTIME!' His curtains are now closed when exterior light is lower than interior light.

What if it's just a cloud passing by the sun? Should the blinds flap open or closed for transient changes? If the motor is slow enough, that's a moot point but otherwise it might be worthwhile to average lighting measurements over time in addition to hysteresis.

If everything is optimized for moving really slowly, it might help battery life to sleep.

With a little more thought, it could get even more ridiculously complicated. Status of light switch? Override for dark preference? Clock/calendar settings? Motion sensors for unoccupied rooms? Network control and reporting? Solar battery charging? (is this an actual project or just a lab exercise to demonstrate how to control a motor with a light sensor?)

To answer your question, this an intro level engineering course, where the aim of the project is simply to design a working circuit that performs some function of my choosing. I'm trying to emphasize this: it is an introductory class and I am new to circuits and coding -- I have been using an Arduino for about one month's time. The fact that I've come this close to making something that resembles something works is amazing to me, and I am proud of myself for accomplishing this much. But my only problem currently is that the motor keeps turning every time the value of the light sensor changes, as opposed to the motor turning only when it crosses the threshold. That's it. If I can nail that problem down, everything else seems to be just fine. Seems to be, at least. All these other considerations of when the light gradient is too steep, or if its night time vs. day time or etc., they're irrelevant. I simply want to demonstrate understanding of the fundamentals of circuits and coding by having a device perform a function I chose. It's very rudimentary, yes. But the details will come later, when I'm more advanced in my studies.

engamir27:
No I did not incorporate limit switches. My reasoning is this: instead of using a limit switch to prevent turning I wanted to calibrate the time the motor turns precisely by using some math to convert the rotations of the motor into distance of string being moved in the open/close direction. Does that make sense?

That makes perfect sense. But you neglected to answer Blackfin's other question: what happens if the motor is trying to open the blinds farther when they're already fully open? It's not something that should happen, but when theory and reality collide, guess which one wins? You might get extra credit on your project if you design for this .... or the instructor might expect that you WILL design for it...
S.

Good engineering knows of Murphy.

If something can possibly go wrong, it will do so.

You need to learn about hysteresis since that's how digital inputs work!
The old time thermostats all have 2 setpoints, those are the example I first saw.

Perhaps this will illustrate my problem more clearly. Instead of my circuit, let's consider a simpler alternative circuit.

Let's consider a circuit with 2 LEDs, one green and one red, in parallel. Green is wired to pin 2, and red to pin 3. Also, there is a 10k pot. Now add a threshold value of 200. Let's say we want the red LED to come on when the pot is above the threshold 200. And we want the green to come on when the pot is below 200. We could write out a program as I have below to perform this function.

Here is the question. Instead of having the LEDs turn on continuously, how could we get them to turn on for an arbitrary period of time, and then turn off unless the threshold has been crossed. In other words, we want the LEDs to turn on for a set time, say 1 second, and then go off. Threshold is crossed, respective LED turns on for 1 second and then turns off. Threshold is crossed again, opposite LED turns on for 1 second and then turns off. How can we accomplish this?

const int analogPin = A0;
const int redLED = 3;
const int greenLED = 2;
const int threshold = 200;

void setup(){
pinMode(analogPin, INPUT);
pinMode(redLED, OUTPUT);
pinMode(greenLED, OUTPUT);
Serial.begin(9600);
}

void loop(){
  int analogValue = analogRead(analogPin);
  Serial.println(analogValue);
    if (analogValue > threshold) {
    digitalWrite(redLED, HIGH);
    digitalWrite(greenLED, LOW);

  }
  else {
    digitalWrite(redLED, LOW);
    digitalWrite(greenLED, HIGH);

  }
  delay(0.5);
}

engamir27:
To answer your question, this an intro level engineering course, where the aim of the project is simply to design a working circuit that performs some function of my choosing. I'm trying to emphasize this: it is an introductory class and I am new to circuits and coding -- I have been using an Arduino for about one month's time. The fact that I've come this close to making something that resembles something works is amazing to me, and I am proud of myself for accomplishing this much. But my only problem currently is that the motor keeps turning every time the value of the light sensor changes, as opposed to the motor turning only when it crosses the threshold. That's it. If I can nail that problem down, everything else seems to be just fine. Seems to be, at least. All these other considerations of when the light gradient is too steep, or if its night time vs. day time or etc., they're irrelevant. I simply want to demonstrate understanding of the fundamentals of circuits and coding by having a device perform a function I chose. It's very rudimentary, yes. But the details will come later, when I'm more advanced in my studies.

Well, your original program remembers the previous light level and then compares the new light level to the old light level. However your original program does not remember if the previous state was above the threshold or below. Your program opens the blinds a set amount and then tests on the next round if that was enough.

That seems like a good method if you want multiple openings until enough light is let in. But if you have only one open target and one closed target, that is an incorrect method.

edit - I wrote this while you posted the above response but I think it still stands.
second edit - your program does not remember if the previous action was open or close(flash red or green).

engamir27:
I want to say thank you for taking the time to write all that out for me and prove me with an example of the code you think is best for my circuit.

No I did not incorporate limit switches. My reasoning is this: instead of using a limit switch to prevent turning I wanted to calibrate the time the motor turns precisely by using some math to convert the rotations of the motor into distance of string being moved in the open/close direction. Does that make sense?

It does but it's a simplistic approach. As a "beginner" level project that's fine but as a (future) engineer you should approach a project in a more broad-minded way. When you first power the project on, will the blinds always be in a known position? What if the power goes out (or the battery dies) and the Arduino wakes up from cold and dark? Does 'x'-seconds of operation still open and close the assembly completely and safely and without hazard?

Would the motor always turn at the same rate? Could brush/commutator wear slow or dust or drying lube in the mechanics cause binding that would change the precise relationship you established when things were brand new?

Not trying to be difficult. Just approaching it as an engineer might.

edmcguirk:
Well, your original program remembers the previous light level and then compares the new light level to the old light level. However your original program does not remember if the previous state was above the threshold or below. Your program opens the blinds a set amount and then tests on the next round if that was enough.

That seems like a good method if you want multiple openings until enough light is let in. But if you have only one open target and one closed target, that is an incorrect method.

edit - I wrote this while you posted the above response but I think it still stands.
second edit - your program does not remember if the previous action was open or close(flash red or green).

I see what you're saying. That makes sense. So I need to tell the program to compare the previous action to the current action. If they are the same, do nothing. If they are different, do something. Something along those lines, if I'm understanding you correctly.

Blackfin:
It does but it's a simplistic approach. As a "beginner" level project that's fine but as a (future) engineer you should approach a project in a more broad-minded way. When you first power the project on, will the blinds always be in a known position? What if the power goes out (or the battery dies) and the Arduino wakes up from cold and dark? Does 'x'-seconds of operation still open and close the assembly completely and safely and without hazard?

Would the motor always turn at the same rate? Could brush/commutator wear slow or dust or drying lube in the mechanics cause binding that would change the precise relationship you established when things were brand new?

Not trying to be difficult. Just approaching it as an engineer might.

I completely understand! Trust me, I know. The real world is not ideal, and any relevant factor should be taken into consideration. Trust me, I wouldn't try to hawk this design over to a company. That's not its purpose. And yes, in a real world setting I would want to produce the best design possible. At the moment, however, I'm just trying to master the fundamentals so that later in my education I can build upon these concepts.

engamir27:
Perhaps this will illustrate my problem more clearly. Instead of my circuit, let's consider a simpler alternative circuit.

It does, just not how you think.

Let's consider a circuit with 2 LEDs, one green and one red, in parallel. Green is wired to pin 2, and red to pin 3.

and each LED has a current limiting resistor of about 220 Ohms or more. 1K to 5K are good for indicator lights.

Also, there is a 10k pot. Now add a threshold value of 200. Let's say we want the red LED to come on when the pot is above the threshold 200. And we want the green to come on when the pot is below 200. We could write out a program as I have below to perform this function.

Here is the question. Instead of having the LEDs turn on continuously, how could we get them to turn on for an arbitrary period of time, and then turn off unless the threshold has been crossed. In other words, we want the LEDs to turn on for a set time, say 1 second, and then go off. Threshold is crossed, respective LED turns on for 1 second and then turns off. Threshold is crossed again, opposite LED turns on for 1 second and then turns off. How can we accomplish this?

I promise that you don't want to use a single threshold in The Physical World.

Between the pot and the ADC, the digitized value may dither 2 or more points at many Hz with no change on the analog signal.
If you divide analog reads by 4 you get more stable reads over range 0-255.

And perhaps 10x a second you read the sensor and act, the motor will run 0.1s before stopping.

if ( millis() - startMs >= waitMs ) // everything inside of this if ( trigger ) { is a task }
{
brightness += analog read sensor / 4

If brightness >= tooBright // tooBright must be at least tooDark + 5
turnMotor = -1
else if brightness <= tooDark
turnMotor = 1
else
turnMotor = 0

startMs += waitMs;
}

if ( turnMotor != prevMotor ) // motor control as an Output Task
{
if ( turnMotor > 0 )
{
read open limit contact
if open limit switch is open // limit not reached
{
open the blinds
}
else
{
turnMotor = 0;
}
}
else if ( turnmotor < 0 )
{
read close limit contact
if close limit switch is open // limit not reached
{
close the blinds
}
else
{
turnMotor = 0;
}
}
else
{
stop the motor
}
else // the limit switches will get checked the most
{
read close limit contact
if close limit switch is closed // limit reached
{
turnMotor = 0;
}
else if open limit switch is closed // limit reached
{
turnMotor = 0;
}
}
}

startMs and waitMs should both be unsigned long variables

brightness, tooBright and tooDark can all be byte variables
turnMotor and prevMotor can be short variables or chars or ints

There's enough to avoid the worst without doing it for you.

The 1) link below has a tutorial on this method. It's not my work, it is really well done and complete.
The last two lines are my take on automating processes asynchronously.

engamir27:
I completely understand! Trust me, I know. The real world is not ideal, and any relevant factor should be taken into consideration. Trust me, I wouldn't try to hawk this design over to a company. That's not its purpose. And yes, in a real world setting I would want to produce the best design possible. At the moment, however, I'm just trying to master the fundamentals so that later in my education I can build upon these concepts.

How many extra motors and H-bridges do you have?

A switch is just a broken wire that can make and break contact.

Can't you imagine your device opening the blinds fully? What kind of failure will it get just testing it out?

You picked a real good course of study but my HS physics teacher had been a EE who saw himself working deeper and deeper into specializing, so he went into education. I took that course 48 years ago.

Blackfin:
First, if you haven't done so look up the term "hysteresis."

hard to imagine an engineering school that would not have taught this very early on.

thinking about the big picture.

if the sun is rising and you expect it to be too bright, then you would want to close the blinds
if the shadow of a tree passes the sensor, then the blinds may open, only to close a minute later.

same thing with clouds. there are transient things that can effect the light levels.

That being said, you want to incorporate a time period of inactivity after some activity.
say, 10 minutes ?

as you should have looked up the word hysteresis and also watched a youtube video on it, you should have a very firm grasp of the word.

we tend to think of hysteresis in a short term so that the device does not dither back and forth.
but the concept can apply to longer time scales. clouds passing, etc.

the user should also have some ability to choose the operation.
a short amount of light will close the blinds, but an extended dark period before opening.

Topics on the same subject merged to avoid duplication of effort

Cross-posting is against the rules of the forum. The reason is that duplicate posts can waste the time of the people trying to help. Someone might spend 15 minutes (or more) writing a detailed answer on this topic, without knowing that someone else already did the same in the other topic.

Repeated cross-posting will result in a timeout from the forum.

In the future, please take some time to pick the forum board that best suits the topic of your question and then only post once to that forum board. This is basic forum etiquette, as explained in the sticky "How to use this forum - please read." post you will find at the top of every forum board. It contains a lot of other useful information. Please read it.

Thanks in advance for your cooperation.