A latching trigger

Hi everyone,

I'm working on a project to make some automatic blinds. They will open and close automatically as it gets light and dark, and also have 2 buttons for manual positioning.

My code works by the buttons changing a variable from 0 to 1 or 2. Depending on wether the variable is 1 or 2 the motor control statement drives the motor in the direction required, or stops it if the variable is 0. I then have a counter which is changed by the values from a rotary encoder. When the counter reaches the set limits in either direction (up or down) it sets the variable to 0.

I have working code for the buttons on their own, and I can get them to open and close with just the light sensor, but not both. I can't work out how to make the light sensor trigger it to close only the first time it detects it's over the light threshold, or below it. At night, when I press the button it opens and then closes again as the light sensor is constantly trying to close them. The opposite during the day.

I have tried making variables for whether it's light or dark and and set it up like a button, referencing its old state etc, but with no joy. I'm pretty new to this and it's probably something simple I'm missing or not using!

Thanks for reading this over, any help would be greatly appreciated!

Tom

Read this before posting a programming question

Thanks for replying. I have included my code which is the buttons working on their own, it doesn't have the LDR part in it as with this code I have never managed to make it work properly, but this is the code I want to add it too. I tired using the same idea as the buttons when I did try to make it work. PWA is the pulse width modulation pin for the motor (Motor A on the arduino motor shield). The set button is so I can set the encoder position so I can adjust the blinds without having to take the hardware apart to adjust its start point.

#define UPbutton 5
#define DOWNbutton 6
#define SETbutton 7
#define PWA 3
#define DIRA 12

int up = 0;

int old_up = 0;

int down = 0;

int old_down = 0;

int set = 0;

int state = 0;

//encoder
const int encoderPinA = 4;
const int encoderPinB = 2;
const int encoderStepsPerRevolution=200;
int angle = 0;

int val;

int encoderPos = 0;
boolean encoderALast = LOW;  // remembers the previous pin state
//encoder end

void setup()
{
  pinMode(UPbutton, INPUT);
  pinMode(DOWNbutton, INPUT);
  pinMode(SETbutton, INPUT);
  pinMode(PWA, OUTPUT);
  pinMode(DIRA, OUTPUT);
  
  //encoder
  pinMode(encoderPinA, INPUT);
  pinMode(encoderPinB, INPUT);
  digitalWrite(encoderPinA, HIGH);
 digitalWrite(encoderPinB, HIGH);
  Serial.begin (9600);
  //encoder end
}

void loop()
{
  up = digitalRead(UPbutton);
  down = digitalRead(DOWNbutton);
  set = digitalRead(SETbutton);
  
  if((up == HIGH) && (old_up == LOW))
  {
    state = 1 - state;
    delay(10);
  }
  
  if((down == HIGH) && (old_down == LOW))
  {
    state = 2 - state;
    delay(10);
  }
  
  old_up = up;
  old_down = down;
  
  if(state == 0)
  {
    digitalWrite(DIRA, LOW);
 analogWrite(PWA, 0);
  }
  
  //state 1 is upward motion
  
  if(state == 1)
 {
     digitalWrite(DIRA, HIGH);
 analogWrite(PWA, 255);
 }
  
  //state 2 is downward motion
  
  if(state == 2)
 {
     digitalWrite(DIRA, LOW);
 analogWrite(PWA, 255);
 }
 
 //autostop bit
 
 if((encoderPos < 10) && (state == 1 ))
 {
  state = 0; 
 }
 
 if((encoderPos > 600) && (state == 2))
 {
   state = 0;
 }
 
 //autostop bit end
 
 //encoder
 boolean encoderA = digitalRead(encoderPinA);

if ((encoderALast == HIGH) && (encoderA == LOW))
  {
    if (digitalRead(encoderPinB) == LOW)
    {
      encoderPos++;
    }
    else
    {
      encoderPos--;
    }
    angle=encoderPos;
    
    Serial.print (state); //only new bit
    Serial.print (encoderPos);
    Serial.print (" ");
    Serial.println (angle);
  }

  encoderALast = encoderA;
  //encoder end
 
 if(set == HIGH)
 {
   encoderPos = 20;
 }
 
}

The code you posted suggests that you are using external pulldown resistors for the switches. Are you?

state is a lousy name for a variable. Something meaningful would be much better.

    state = 1 - state;
    state = 2 - state;

I can't figure out what state is supposed to be when this code is done. Or what it is supposed to represent. Or, how that works when state can supposedly have three values.

Why do you use the value of encoderPos and then check the encoder? I would think that it would make more sense to check the encoder first, then use encoderPos.

Hi PaulS,

Thanks for replying. Yes, my switches have resistors fitted to them, this was following the example in the Arduino book I have. Thanks for your advice, I'll change the variable state to something more meaningful! :slight_smile:
My basic idea with this was to have the variable state, and depending on the value of this variable have the motor do something. So 0 is stop, 1 is up and 2 is down. The buttons change the variable state to initiate the motor code. The reason for the state = 1 - state bit was also from the Arduino book, which was about making the buttons so you just have to press them once rather than keep them pressed to open or close the blinds, and then either the auto stop bit kicks in when the blinds are open, or the second button press returns the variable state to 0 so you can have the blinds partly open etc.
I'll be honest the encoder bit was from an example from a different book, I don't really know about it, it works so I didn't want to touch it and for it to then stop working.

Why use an encoder rather than a standard potentiometer? Seems like as a beginner to coding this sort of stuff processing the analog in from a pot will be much easier than following the flow of an encoder.

How are your light sensor set up? Voltage divider?

To save you some time in your coding i will tell you that

if((up == HIGH) && (old_up == LOW))

is the same as

if(up && !old_up)

I think you are on the right track with the states, but you do not have enough of them for easy debugging.

How about these names for states (you may think of others for your application)
IDLE, OPENING, FULLY_UP, CLOSING, FULLY_DOWN.

Now try writing down what you want it to do for each state transition, and what will start the transition. Try and cover
the cases from all sensor inputs that are available, including your buttons. That will give the logic of what happens at
each state, and should make it easy to code. As part of what will happen, decide on which next state is wanted.

For example:
Its IDLE, and light sensor says dark => switch to CLOSING

Its FULLY_DOWN and light sensor says dark => do nothing
Its FULLY_DOWN and light sensor says daylight => switch to OPENING

Its OPENING and not at encoder limit => motor up
Its OPENING and at encoder limit => motor off, switch to FULLY_UP
Its OPENING and down_button pressed => do nothing (or if that is the wrong spec for you, say something else).
Its OPENING and light sensor says dark => do nothing
Its OPENING and light sensor says daylight => do nothing

Then try drawing out a state diagram (optional, but really useful). In your case, for each state there are inputs from the sensor, encoder and buttons.

Don't try "clever" tricks like state = state -1, explicitly set the next state wanted, e.g. state = OPENING;
(A useful, but advanced idea is a C++ enum).

Hi,

Thanks for replying. I am using an encoder as I didn't think a potentiometer would be accurate enough as I wanted precise positioning. I admit, as a newbie they are not the simplest, and I do not fully get everything the code does in relation to them, but as far as I can guess that bit is working fine, and functioning as intended.
I am using just one light sensor wired up and being read by an analog pin. As I have made a test rig for the setup I can easily control the amount of light to the sensor, but when it's live I think I will have two set up like a divider to make it less likely to trigger by things like car headlights going past.
Thanks for the advice, I will change the variables to be easier to understand and more descriptive, and I'll do a flowchart too.
The big issue and I can't get is the trigger. So I can get it to change the state to 'OPENING' when it gets light, but I don't know how to just do this just once. As when I then close it partly with the buttons it opens again because the light sensor logic is saying 'it's light, but the blinds aren't open, so set to 'OPENING' '. I would like help with the code needed to say ok its light, so open, but then don't try to open again until it has gone dark again first.

thanks

Tom