Help with While and subroutines

Hi All

I am working on an old project to make it more advanced (While in Lockdown here in NZ). It is an automatic chicken door that opens when it is light and closes 2 hours after the low light setting (to allow the chickens time to get in). Currently it has mechanical switches to set to manual and drive the door up with a DPDT relay. I want to do this via programming and learn more about subroutines as I have never used them before.

So far I have just added each component to the design at a time (first the LCD and buttons, then the latching relay to switch the motor direction), then tested the program then move on, hence there are a lot of unused pins and int's that are commented out. I am stuck trying to call a subroutine for a while loop the sets a max run time for the motor.

My problem is that I want the motor that drives the door to only run for 30 seconds (currently programmed a 3 secs for testing) the door will have limit switches so the max run time is a backup safety feature if the door gets jammed to prevent the motor burning out.

When I run the ManMode() subroutine and press a button which starts a while loop, the while loop then sets the time that is passed to the motor run subroutine. However this seems to pause the while loop for 3 seconds before then activating the latching the relay. It also doesn't cut the relay after 3 seconds, it just stays on. I assume the while is running over and over and I haven't got some form of break in the correct place. I thought of changing the void ManMode() to an int ManMode() and then passing a 1 or 0 back to whatever calls it but I don't know if that would work either.

I have tried several different ways but noting quite works.

I am looking for some examples to get me over this learning hurdle, anything would be appreciated

How do I
"While button is pressed"

  • Activate latching relay coil to drive motor forward or backwards (got this bit going fine)
  • Set a time the the motor has started from the time the button was pressed
  • Only allow motor relay to run for 30 secs before switching off but continuing original while loop
  • Print to LCD ERROR if 30 secs reached (I can probably work this out myself)

And lastly be able to call the Motorrun() subroutine from other parts of the sketch as it will be used in auto mode as well when the door is signaled to close or open.

Cheers
Al

#include <Wire.h>                  // Include Wire library (required for I2C devices)
#include <LiquidCrystal_I2C.h>     // Include LiquidCrystal_I2C library 

//Pins
//int LDRsensor =         // LDR sensor used in auto mode
int Relayup = 5;          // latching relay for coil 1
int Relaydn = 6;          // latching relay for coil 2
//int Reedup =            // switch to detect door has reached full heigh
//int Reeddn =            // switch to detect door fully lowered
int Motorrelay = 7;       // motor relay set to 30 seconds to prevent motor burning out in case of jam
//int RedLed =            // Indicator light door up
//int GreenLed =          // Indicator light door down
//int BlueLed =           // Indicator light door in countdown process

int AutoManButton = 2;    // button to set auto and manual
int Buttonup = 3;         // button to drive door up in manual mode
int Buttondn = 4;         // button to drive door down in manual mode

//Varibales
//int sunset = 400;                     // Low light threshold to close door
//int sunrise = 520;                    // upper light threshold to open door
//unsigned long Closedelay = 10000;     // interval added to current time to close the door
//unsigned long sunsettime;             // sets a varible to hold the time the light reached low threshold
//unsigned long doorclose;              //  sets a varible to hold the time to close the door after low light threshold
//int Doorposition;                     // Stores the current door position 0 open 1 closed
//int Lightvalue;                       // Sets the varible to hold the LDR level
int Mode = 0;                           // Set if auto or manual mode 0 is manual and 1 is auto
int ModeDisp;                           //Display the Mode as Text
int AutoManualMode = 0;                 // stores the mode - double up of mode int and possibly can be removed
unsigned long Motorstart;               // sets the time the motor started used for max run
unsigned long Motorrunmax = 3000;       // defines how long the motor can run



LiquidCrystal_I2C lcd(0x27, 16, 2);     // Configure LiquidCrystal_I2C library with 0x27 address, 16 columns and 2 rows


void setup() {
  Serial.begin(9600);
  pinMode(Relayup, OUTPUT);             // pin for latching rely up
  pinMode(Relaydn, OUTPUT);             // pin for latching relay down
  //    pinMode(Reedup, INPUT);         // pin to detect when door has reached up
  //    pinMode(Reeddn, INPUT);         // pin to detect when door has reached bottom
  pinMode(Motorrelay, OUTPUT);          // pin to isolate motor
  //    pinMode(RedLed, OUTPUT);        // LED red output
  //    pinMode(GreenLed, OUTPUT);      // LED green output
  //    pinMode(BlueLed, OUTPUT);       // LED Blue output
  pinMode(AutoManButton, INPUT_PULLUP); // Button to switch between auto and Manual
  pinMode(Buttonup, INPUT_PULLUP);      // Button to drive door up in manual
  pinMode(Buttondn, INPUT_PULLUP);      // Button to drive door down in manual

  lcd.init();                        // Initialize I2C LCD module
  lcd.backlight();                   // Turn backlight ON - need to add time out on backlight

}

int Setmode() // subroutine to set a global int to auto 1 or manual 0 via buttom press
{
  int buttonpress = digitalRead(AutoManButton); // read button
  delay(500);                     // pause so button only read once - look at other options to only read button a single time

  if (buttonpress == LOW) {       // if button pressed
    if (AutoManualMode == 0) {    // if in manual mode
      AutoManualMode = 1;         //switch to auto mode
    }
    else
    {
      AutoManualMode = 0;          // switch to manual
    }
  }
  return AutoManualMode;           // return the result to Setmode
}

void loop() { // main loop

  Mode = Setmode();       // set the mode - this int is a double up maybe consolidate

  if (Mode == 1) { // if mode is auto
    lcd.setCursor (0, 0);
    lcd.print ("Auto  ");
    // AutoMode();         // run subroutine for automatic mode
  }

  if (Mode == 0) {
    lcd.setCursor (0, 0);
    lcd.print ("Manual");
    ManMode();               // run subroutine for manual mode
  }
}

void ManMode()
{
  digitalWrite(Relayup, LOW);                // default relay low so no power to relay coil
  digitalWrite(Relaydn, LOW);                // defealt relay low so no power to relay coil
  digitalWrite (Motorrelay, LOW);            // default motor relay to low - motor off


  while (digitalRead (Buttonup) == LOW) {    // while button up is pressed // need to add && digitalRead upswitch == LOW
    Motorstart = millis();                   // set motor start time - is this being constantly reset during while?
    Motorrun(Motorstart);                    // subroutine to run motor for max 30 secs
    digitalWrite (Relayup, HIGH);            // latch coil on relay to switch motor to forward direction
    lcd.setCursor (0, 1);
    lcd.print ("Door Going UP");             // print to LCD door going up
  }

  while (digitalRead (Buttondn) == LOW) {    //&& digitalRead upswitch == LOW
    Motorstart = millis();
    Motorrun(Motorstart);
    digitalWrite (Relaydn, HIGH);            // set latching coil to reverse motor contacts
    lcd.setCursor (0, 1);
    lcd.print ("Door Going DN");
  }

  if (digitalRead (Buttonup) == HIGH || digitalRead (Buttondn) == HIGH) { // if both buttons are open
    lcd.setCursor (0, 1);
    lcd.print ("              ");                                         // over type to clear LCD
  }
}

void Motorrun(int Motorstart)               // subroutine to switch relay to motor for 30 seconds, recieves start time used in manual and auto mode
{
  //int Motorstart = millis();
  int Motorruntime = Motorstart + Motorrunmax;   // calculate max run time
  digitalWrite (Motorrelay, LOW);           // set default to low


  while (millis() <= Motorruntime) {        // while current time is less then motorruntime // need to add in door swith condition here also
    digitalWrite (Motorrelay, HIGH);        // turn on relay
    if (millis() > Motorruntime) {          // if current time is reached add in print LCD error
      break;                                // break out of while loop motor relay should be low as defalut
    }           
  }
}

Suggest you avoid do/while loops like a virus ???


These can block code execution if not used properly.


Take a look at the ‘State Change Detection’ example in the IDE.


Have you heard about ‘State Machine’ programming ?


FYI

Please send chicken and or eggs . . .

Thanks Larry

Good to know what I'm doing is actually hard, it definitely seems that way and I could see it interrupting the code.

I haven't heard of state machine programming, I come from an Architectural (Buildings) background rather than computer sciences so I'm still feeling my way. I have googled it now and will look some more.

Your schematics are intriguing, they are similar to what I have built so far but mine are a lot more basic, which works fine expect the motor is held in a bracket that if the door jams it is possible the motor can turn given enough force and then wind itself up on its leads. The motor then stalls but draws current and then flattens the battery.
So it is a failure of mechanical engineering rather than programming :slight_smile: I could simply adjust the original code and add a relay in but I seem to have 4 weeks of lockdown to kill so thought I'd make it as complicated as I can.

Re Eggs: Just send self address egg carton :slight_smile:

Chicken door r3.pdf (78 KB)

This might make the juices flow.

State Machine and Timers, Medium level tutorial.
https://forum.arduino.cc/index.php?topic=525240.0


Thanks larry

I will start working through that and see how to apply it, this is exactly the sort of thing I wanted to be able to learn more rather than just have whatever I'm doing wrong "fixed"

Cheers and stay healthy

Al

Try to convert your code (or rewrite) to a State Machine sketch.

If you run into problems lots of people here can help.

Please explain the schematic symbol for the latching relay.

Hi Larry

I'm not sure what you mean, I didn't realise I would be tested :slight_smile:

I see there is a mistake in my sketch though that D3 is suppose to be onto the positive of the forward coil of the relay not the common terminal, it is physically built correctly though.

This was was my first ever attempt at real electronics and programming that wasn't clumsy 12v automotive relays or the blink sketch. Certainly a lot I would change now as currently the coils remain energised when they only need pulse to move the relay switch. I could, in hind sight, have done that straight off an output of the ATMega chip rather than introducing Relay 1 to reverse relay 2

FYI this the code that accompanies it which I'm now rewriting.

// Pins
int sensorPin = 0;
int relayPin = 2;
int LEDtimer = 8;

// Variables
int lowThreshold = 400;          // Low light threshold to close door
int highTreshold = 520;          // upper light threshold to open door
unsigned long DoorDelay = 5000; // interval added to current time to close the door
unsigned long currentMillis;    // sets a varible to hold the time the light reached low threshold
unsigned long Doorclose = 0;    // sets a varible to hold the time to close the door after low light threshold
int DoorPosition = 0;          // Stores the current door position 0 open 1 closed
int sensorValue = 0;           // Sets the varible to hold the LDR level

void setup() {

  // Start Serial & set pin to output
  Serial.begin(9600);
  pinMode(relayPin, OUTPUT); // relay to switch the latching realy to drive the motor up or down
  pinMode(LEDtimer, OUTPUT); // 3mm LED to show that the count down has started


}

void loop() {

  // Read the sensor pin
  sensorValue = analogRead(sensorPin);

  // If light level is low is detected, set the time to shut the door
  if (sensorValue < lowThreshold) {

    if (DoorPosition == 0) {   // Test door is currently open

      if (Doorclose > 0) {  // Test the door set time has been set

        if (millis() >= Doorclose) { // check if door shut time has been reached
          digitalWrite(relayPin, LOW);  // close door
          Doorclose = 0; // Set door close variables back to zero
          DoorPosition = 1;  // set the door position to closed
          digitalWrite(LEDtimer, LOW); // Turn off count down LED 
        }
      }
      else
      {
        // If door set time has not been set

        currentMillis = millis();  //get the current "time"
        Doorclose = currentMillis + DoorDelay; // set the time to close the door from current time plus 1 hr
        digitalWrite(LEDtimer, HIGH); // Start count down LED
      }
    }
    // if door is closed alread do nothing
  }



  // If light level goes up again, open the door
  if (sensorValue > highTreshold) {
    digitalWrite(relayPin, HIGH);
    DoorPosition = 0; // set door position ready for door close if statement
    Doorclose = 0; // Set door close variables back to zero, this resets the countdown if the light goes back up
    digitalWrite(LEDtimer, LOW); // turn off cout down LED
  }

}

No test, it’s just that my dual coil symbol is different than yours.

In you schematic, if the lower left pin is pin #1 is pin 3&8 the common, pin 4&9 N.C., and pin 5&6 N.O. ?


It’s always best to add hysteresis when you are looking at an analog signal controlling things.

It is FRT3-SL2 mechanical latching relay so it doesn't have a NC or NO as such as whatever state it was left in it will stay that way. I use forward and back but I believe proper term is set and reset. Data sheet is attached

from my sketch it would be

1 - Coil back
2 - Coil forward
3 - common Pole 2
4 - back Pole 2
5 - Forward Pole 2
6 - forward pole 1
7 - back Pole 1
8 - Common pole 1
9 - Coil forward
10 - Coil back

DSASW00217586.pdf (41.2 KB)

Thank you.


Okay, I now see you have two thresholds.
int lowThreshold = 400; // Low light threshold to close door
int highTreshold = 520; // upper light threshold to open door


Please don’t us PDFs.
JPG images are better as they can be displayed directly.

@Allan_Pritchard
Would you explain what is supposed to happen when the sensor goes from:
'full light to 'full dark'.

I assume you mean once the sensor drops to a value of 399

Then the program first checks the door is open by the DoorPosition int. If its closed then do nothing

Then checks the time has been set for the door to close, by checking if the doorclose varible is greater than zero, if not it sets the time, this is 5000 in the test sketch but is set to 7,200,000 on the actual door allowing 2 hours for the chickens to get in. I didn't make that clear in the coding.

if the timer has been set it waits for the time elapsed before shutting the door. (secs for testing and 2 hours in reality)

Then it resets everything back to a state ready for next time.

I have crudely used integers 1 and 0 to test if certain cases are meet rather than doing it properly with the likes of bool or switch cases which only just finding out about.

I am having trouble with a certain part of the example you gave me in post #3

I can't work out how the line

boolean CheckTimer(makeTimer &TimerX)

I think I understand the boolean defines it as the true/false

but I cant work the &TimerX

I worked out the & is a pointer but I can't work out how the X works in this part of the code.

I guess it relates to each defined timer that is further defined by the structure makeTimer.

So makerTimer Timer1 and the pointer is going to that but how does the X work to look at Timer1, Timer2, Timer3 etc.

Sorry if its glaringly obvious

Cheers
Al

Let's forget about the example for now as it does take some time to understand.


Here a sketch that is a bit more intuitive.

It will not be what you want but its a starting point.


Why don't just energize the relay module to close the door ?

Then de-energize the relay module to open the door ?


Check the connection of D3 in the schematic.

It’s best to use JPG images.

//**********************************************************************
//  FileName.ino
//
//  Version   YY/MM/DD     Comments
//  1.00      20/03/20     Running code
//
//**********************************************************************


#define LEDon         HIGH       //pin---[220R]----[>|]---GND
#define LEDoff        LOW

#define isPUSHED      LOW        //INPUT_PULLUP---pin---[N.O. switch]---GND
#define isRELEASED    HIGH

#define enable        true
#define disable       false

#define CLOSE         LOW
#define OPEN          HIGH

#define DoorIsClosed  HIGH
#define DoorIsOpen    LOW

#define lowThreshold  400        //Low light threshold to close door
#define highTreshold  520        //upper light threshold to open door

//**********************************************************************
// A N A L O G S
const byte sensorPin     = A0;

// I N P U T S

// O U T P U T S
const byte heartbeatLED  = 13;
const byte doorRelay     = 2;

// S R A M

byte DoorPosition        = 0;         //Stores the current door position 0 open 1 closed
int sensorValue;                      //Sets the varible to hold the LDR level

//timing stuff
unsigned long heartbeatMillis;
unsigned long analogMillis;
unsigned long DoorMovingMillis;       //the time when the door was told to close
const unsigned long DoorDelay = 5000; //the time to stop the motor

// E E P R O M


//**********************************************************************
void setup()
{
  Serial.begin(9600);

  //LED that toggles and shows if code is non blocking
  pinMode(heartbeatLED, OUTPUT);
  
  //relay to switch the latching relay to drive the motor up or down
  pinMode(doorRelay, OUTPUT);
  
} //END of setup()

//**********************************************************************
void loop()
{
  //****************************
  //Heartbeat LED will flash if there is no code blocking
  if (millis() - heartbeatMillis >= 200)
  {
    //restart the timer
    heartbeatMillis = millis();

    //Toggle heartbeat LED
    digitalWrite(heartbeatLED, !digitalRead(heartbeatLED));
  }

  //****************************
  //time to check the Analogs ?
  if (millis() - analogMillis > 100)
  {
    //restart the timer
    analogMillis = millis();

    checkAnalogs();
  }

} //END of loop()


//**********************************************************************
void checkAnalogs()
{
  //Read the sensor voltage
  sensorValue = analogRead(sensorPin);

  //****************************
  //Is it night time ?
  if (sensorValue < lowThreshold)
  {
      digitalWrite(doorRelay, CLOSE);
  }

  //****************************
  //Is it day time ?
  if (sensorValue > highTreshold)
  {
      digitalWrite(doorRelay, OPEN);
  }

} //END of checkAnalogs()

//**********************************************************************

My thought process is that using the latching relay to say pin 12 and 13 for example I can reverse the motor up or down. Then all I need to do is control a simple NO/NC relay to turn power on to the motor.

so basically exactly as you were saying

I could set the time on/off exactly to match the door closing speed but I would prefer to have an upper and lower switch. These currently are a reed switches but any "switch" will do and I may go magnetic. These physically cut power to the motor at present but I want to do it via programming.

as i understand these would go into the "INPUTS" in the sketch you just sent then just need to test if that condition is meet to stop the door either up or down.

Then I will also have a mechanical cutout switch so if the door overruns it physically drops power to the motor, for the just in case scenario.

That in essence is all I need as I already have a manual overide to raise or lower the door but thought why stop there why stop there :slight_smile:

So I wanted to add the LCD and some controls. This is where I ran into trouble with the While code blocking the sketch. I have a protype on my desk which just drives LEDs and thanks to your help have that working with the ChangeStateDetection. I'm now looking at State Machine Programming and have watched some great videos on what it is and how to apply it plus gone through the example you sent which is very complex for a learner, even the Void setup() took some working out.

I will go through the example you sent, I really learn by pulling things apart so will go through it line by line, it looks a bit more my level.

Further, and not attempted yet is to add a menu to the LCD for setup.
Auto -> Manual -> Setup
Set - Low threshold
Set - High Threshold
Set - Door close delay in secs.

I need schematic to visualize things.

I'll draw something up here.

Will you be use a 238 chip as seen in you schamatic or will you be using a Arduino Pro Mini ?

I'll be using a Nano board as I purchased 30 of them in bulk deal.

Does this sound correct ?

(pins can be whatever #s you desire)

D3 AutoManualSwitch
D4 AutoManualLED (ON = Manual)
D5 ManualDownSwitch
D6 ManualUpSwitch
D7 TimingLED (ON = we are timing)
D8 DoorUpCoil
D9 DoorDownCoil
D10 MotorPowerOnOffRelayCoil
A0 SensorLDR
A4 SDA (LCD)
A5 SCL (LCD)

Yes looks correct along with two extras

D11 - DoorPositionSwitchUP // sensor to indicate door has moved to fully open (up)
D12 - DoorPositionSwitchDN // sensor to indicate door has moved fully closed (down)