Yard Light Help

I know this is probably the wrong way to ask but I am under a time crunch here and just wanted to see if someone could help me out by replying with a program. I am trying to use UNO to control a latching solenoid for an air system. What I need it a program that will work off 1 PB so when it is pressed the SOL will close and stay closed. Next I need the SOL to open and stay open while the same PB is pressed or pressed and held until it is released. I need the program to send a HIGH on one pin to close the SOL and a LOW on other to open but somehow I need the UNO to send a HIGH again once the button is released so it will close the SOL again. I also need the SOL to go open after 5 minutes of no button being pressed then it will go into stand-by mode waiting for the PB to be pressed again.

I am wanting to switch polarity using Darlingtons transformers 2 PNP for on and 2 NPN for off so I need 2 different outputs 1 HIGH for on and one LOW for off.

Is this even possible? I would really appreciate any help someone could lend me with this project. Thanks JXS

You need a "state machine." There's a few tutorials online but there's none I've found yet that really explain the concept and give you a working example code. The problem is that 95% of the code will be yours: highly specific to your application. There's no state machine that you can just change a few numbers at the top and have it do exactly what you want.

Start with a blank sheet of paper. Write "start" or "waiting" in a circle near the top. This is the initial state of the system when you first switch it on. The outputs should probably all be off and it doesn't yet know the status of the inputs.

The core of the state machine idea is the transitions - what makes it move from the "waiting" state into a state that does some action or output? In your case, you're probably looking for a button-push event. But you need to get very specific: you actually need to think of the button transitions as "button down" and "button up" and there may be two states required to handle theses cases.

OK, so when the Arduino detects button-down for the first time, it should move to the "engage solenoid" state. The solenoid's "on" wire should be high. But you only need to hold this high for half a second and the solenoid will latch, correct? So there's two ways to leave the "engage solenoid" state - either the person releases the button or 0.5 seconds has elapsed. These two transitions will move to different states but don't worry, they will come back together soon.

So a state transition can be generated externally (a button) or it can be internal (a timer expired.) Once you start looking at this level of detail, you will end up with a lot of states for a seemingly simple system.

Put that on a piece of paper, scan or photograph it and then we can start writing some code for you.

I hope this is what you had in mind. Please let me know and thank you for your help. JXS
I saved it as an attachment.

jxs058959 - recheck your post; no attachment or anything else shows up (I assume you did as MorganS asked)...

Hope it attaches this time.

Example of a simple state machine, that moves from state to state (indicated by a pair of LEDs) via the pressing of a button (note - the following example has been checked for compilation - but not for operation):

// NOTE: Debounce code is based on the code found at:
//     http://www.arduino.cc/en/Tutorial/Debounce

int ledPin1 = 13; // LED connected to digital pin 13
int ledPin2 = 14; // LED connected to digital pin 14

int buttonPin = 7;   // pushbutton connected to digital pin 7

int buttonState; // the state of the pushbutton
int lastReading = LOW; // initialise the previous reading from the pushbutton

// the following variables are long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 50;    // the debounce time; increase if the output flickers

int machineState; // the state of the state machine

void setup() {
    machineState = 1; // initialize state machine state
}

void loop() {
    int getButtonState = readButton();
    
    switch(machineState) {
        case 1:
            // turn off both LEDs
            digitalWrite(ledPin1, LOW);
            digitalWrite(ledPin2, LOW);
            
            if (getButtonState == HIGH) {
                digitalWrite(ledPin1, HIGH); // turn on first LED
                machineState = 2;
            }

            break;

        case 2:
            if (getButtonState == HIGH) {
                digitalWrite(ledPin2, HIGH); // turn on second LED
                machineState = 3;
            }

            break;

        case 3:
            if (getButtonState == HIGH) {
                digitalWrite(ledPin1, LOW); // turn off first LED
                machineState = 4;
            }
            
            break;

        case 4:
            if (getButtonState == HIGH) {
                digitalWrite(ledPin2, LOW); // turn off first LED
                machineState = 5;
            }
            
            break;

        default:
            // since there is no "machineState = 5" - the switch-case
            // will default and change the machineState to "1"
            machineState = 1;
    }
}

int readButton() {
    // the following debounce code is based on the code found at:
    //     http://www.arduino.cc/en/Tutorial/Debounce

    // read the current state of the button
    int readButton = digitalRead(buttonPin);

    // check to see if you just pressed the button 
    // (i.e. the input went from LOW to HIGH),  and you've waited 
    // long enough since the last press to ignore any noise:  

    // If the switch changed, due to noise or pressing:
    if (readButton != lastReading) {
        // reset the debouncing timer
        lastDebounceTime = millis();
    } 

    if ((millis() - lastDebounceTime) > debounceDelay) {
        // whatever the reading is at, it's been there for longer
        // than the debounce delay, so take it as the actual current state:

        // if the button state has changed:
        if (readButton != buttonState) {
            buttonState = readButton;
        }
    }

    // save the reading.  Next time through the loop,
    // it'll be the lastButtonState:
    lastReading = readButton;

    return buttonState; // return the state of the button
}

In the above example, the state of the machine is represented by an integer value; in a real state machine, you would want to declare a set of named constants to indicate the state, then use those constant names in your state machine transition checking logic - as doing so is easier to understand and debug than a number. However, I was trying to keep this example as simple as possible, while still being operational (hence the debounce code - I hope that doesn't complicate things too much).

Note also that "loop" keeps looping - regardless of the state of the machine; you could easily set things up so that within one of the state check you called a function, and within that function use code similar to the "blink-without-delay" example to cause an action to occur repeatedly - or to check for other stuff happening (like incrementing a value based on the reading of another sensor, perhaps). Basically - while not necessary for a state machine to function - implementing non-blocking code allows you to expand and utilize the processor to the fullest extent (blocking code merely wastes cycles which could be better spent doing something - also, you might miss certain things, like sensor readings, with blocking code).

While the above example shows the state machine transitioning states in an orderly and serial fashion, nothing says this has to be the case. You could jump to any state you want from any other state, based on whatever criteria, logic, or readings you take while in the current state.

Finally - state machines don't have to be implemented as a switch-case. You could, for example, do it all with functions (again - I note that this compiles, but I didn't check the operation - but it should function similarly to the earlier state machine example):

// NOTE: Debounce code is based on the code found at:
//     http://www.arduino.cc/en/Tutorial/Debounce

int ledPin1 = 13; // LED connected to digital pin 13
int ledPin2 = 14; // LED connected to digital pin 14

int buttonPin = 7;   // pushbutton connected to digital pin 7

int buttonState; // the state of the pushbutton
int lastReading = LOW; // initialise the previous reading from the pushbutton

// the following variables are long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 50;    // the debounce time; increase if the output flickers

void setup() {
    machineState1(); // go to first machine state
}

void loop() {
    // nothing to do in here!
}

void machineState1() {
    while (true) {
        // turn off both LEDs
        digitalWrite(ledPin1, LOW);
        digitalWrite(ledPin2, LOW);
        
        if (readButton() == HIGH) {
            digitalWrite(ledPin1, HIGH); // turn on first LED

            machineState2(); // jump to the next state!
        }
    }
}

void machineState2() {
    while (true) {
        if (readButton() == HIGH) {
            digitalWrite(ledPin2, HIGH); // turn on second LED
            
            machineState3(); // jump to the next state!
        }
    }
}

void machineState3() {
    while (true) {
        if (readButton() == HIGH) {
            digitalWrite(ledPin1, LOW); // turn off first LED

            machineState4(); // jump to the next state!
        }
    }
}

void machineState4() {
    while (true) {
        if (readButton() == HIGH) {
            digitalWrite(ledPin2, LOW); // turn off first LED

            machineState1(); // jump to the next state!
        }
    }
}

int readButton() {
    // the following debounce code is based on the code found at:
    //     http://www.arduino.cc/en/Tutorial/Debounce

    // read the current state of the button
    int readButton = digitalRead(buttonPin);

    // check to see if you just pressed the button 
    // (i.e. the input went from LOW to HIGH),  and you've waited 
    // long enough since the last press to ignore any noise:  

    // If the switch changed, due to noise or pressing:
    if (readButton != lastReading) {
        // reset the debouncing timer
        lastDebounceTime = millis();
    } 

    if ((millis() - lastDebounceTime) > debounceDelay) {
        // whatever the reading is at, it's been there for longer
        // than the debounce delay, so take it as the actual current state:

        // if the button state has changed:
        if (readButton != buttonState) {
            buttonState = readButton;
        }
    }

    // save the reading.  Next time through the loop,
    // it'll be the lastButtonState:
    lastReading = readButton;

    return buttonState; // return the state of the button
}

There's ways of doing it in other fashions as well (using if-then-else logic, using pointer transitions, function array lookup tables, and more).

Don't get hung up on what the "right way" of implementing a state machine is - but rather understand what a state machine is, how it works, what it is for, and what it's advantages and disadvantages are compared to other potential solutions to a particular problem. Then, implement the state machine in the manner that best suits the need and solution (and your understanding and style).

jxs058959:
Hope it attaches this time.

I'm going to offer some constructive criticism - while I could take what you diagrammed and make a state machine with it - I'm not going to - because your diagram isn't complete.

For instance, your "Start" isn't really named the true state; if I were naming it, I would call the state "CHECKBUTTONSTART" - the system would sit in that state, waiting for the button press - when it saw that the button was pressed, it would transition to a new state, called "CLOSESOLENOID" - which would set a digital pin HIGH for 5 msec (to close the solenoid) - when the 5 milliseconds passed, the state would transition again (now in your middle section) to a new state - perhaps called "CHECKBUTTONPRESS" - and based on that would either open the solenoid or keep it closed; only when the button is released would it transition to "WAITNOACTIVITY" where it would calculate 5 minutes have passed - after which it would "sleep" (which I am not sure what you mean here - do you mean that the system would stop responding to any button pushes until the Arduino is reset, or do you mean it would actually go back to the beginning state? This needs clarification).

So - as you can see - some of the steps on your diagram are either representing multiple states, or representing their function in an unclear manner.

Refactor your state machine diagram. Name each "state" what it does (in as few words as possible). Show the transitions between states (and any loops if the state needs to re-check for something - like time passing or such).

Once you have the diagram properly documented - you might also try to understand the earlier state machine code I posted - and attempt to alter it to fit your new diagram. But first, update your diagram and post it, then we can see whether it has enough info and proper layout for us to say "ok - now implement the state machine - take a stab at the code".

I'm trying to approach this as a learning exercise, rather than doing the work for you. Good luck - I think you are well on your way to understanding this non-trivial subject! :smiley:

crOsh, I really appreciate the help I hope to be at your level someday. I will look it over it may take me a little while since I am doing on the Ardunio training :). The term "State Machine" is new to me so I am starting to research it tonight. Knowing what you know how long would it take you to write this program? I just ask so 5 years down the road I can gauge my progress. Thanks for your help again. JXS

I don't see the need for the state machine.

a simple h-bridge to run the latching coil.

if the button is pressed and released
send pulse
set flag that solenoid is closed.

since the flag is set, the next pulse will OPEN it (other side of H-bridge)
and set flag solenoid is open

timer runs for 5 minutes
closes solenoid.
and set flag solenoid is open

dave-in-nj:
I don't see the need for the state machine.

a simple h-bridge to run the latching coil.

if the button is pressed and released
send pulse
set flag that solenoid is closed.

since the flag is set, the next pulse will OPEN it (other side of H-bridge)
and set flag solenoid is open

timer runs for 5 minutes
closes solenoid.
and set flag solenoid is open

No - technically, you don't need a state machine for a project this simple. But, if later it is decided to expand the project to implement more states, or other controls, or anything else, setting it up as a state machine in the beginning is going to save a ton of headache later.

Even if that isn't the case - using this simple example to learn about state machines might prove useful down the road on more complex projects.

jxs058959:
crOsh, I really appreciate the help I hope to be at your level someday. I will look it over it may take me a little while since I am doing on the Ardunio training :). The term "State Machine" is new to me so I am starting to research it tonight. Knowing what you know how long would it take you to write this program? I just ask so 5 years down the road I can gauge my progress. Thanks for your help again. JXS

I could probably jam something up in a careful half-hour or so, then spend more time debugging it. If I was quoting this out for a client, it would be a minimum of 8 hours (i'm not saying it would take that long, but when you are doing this kind of work, crap tends to happen that causes your estimates to go out the window - so I try to factor that in).

Note, though, that I've been doing software development for over 20 years professionally now.

It might take you a week to come to grips with things - or it may just "click" for you, and you'll understand the concept fairly quickly.

Perhaps a real world analogy to a state machine might be a set of tasks you do in the morning:

  1. State: Sleeping - Trigger: Alarm - Goto: State 2
  2. State: Waking Up - Trigger: Wait 10 seconds - Goto: State 3
  3. State: Turn Off Alarm - Trigger: Alarm Off - Goto: State 4
  4. etc.

Now - the above is a linear set of states and triggers that cause movement to next state - if the trigger doesn't occur, you "loop" inside that single state until the trigger occurs.

You might have a state where you are doing something, but two or more triggers might exist - they might both lead to the same state, or they might each lead to a different state. For that matter, you might implement a random picking routine that causes a trigger to jump to a random state.

Maybe I'm just babbling here; I'm just trying to relate something "real world" to you, so that maybe you'll pick up the idea quicker. Really, we all use something akin to a "state machine" inside our heads every day as we take in "input", make decisions, and perform actions based on those decisions (which lead to more input, etc).

That's really all a state machine is.

@Dave: The system needs to remember where it is up to. Is the solenoid in the 'on' or 'off' position, so what is the next pin to pulse to make it change position? Even if you don't call this memory "state" it's still a state machine.

The state diagram rapidly gets complex. The simple 3-circle diagram posted crams a lot of actions and decision-making into each circle. I got 7 states when I tried to translate the original description into a state machine and I don't think I have the debouncing correct yet.

The first state should be "what does the machine know when it's first switched on?" It knows nothing. It can look to see if the button is currently held down and then it can transition to a new state depending on what it sees on that input. In that very first state, you don't want to engage any outputs. It should be called "standby" or "sleep", not "start" since it hasn't started doing anything yet.

Hi,
Can you post a link to the spec sheet for the latching solenoid please.

Tom.... :slight_smile: