const byte waitForPressingStart = 0;
const byte moveConveyor = 1;
const byte checkForBucketInPosition = 2;
const byte openWaterValve = 3;
const byte wait5Seconds = 4;
const byte closeWaterValve = 5;
byte myStateVar = waitForPressingStart;
unsigned long WaterFillTimer;
const char stateNames[][32] {
"waitForPressingStart",
"moveConveyor",
// 123456789012345678901234
"checkForBucketInPosition",
"openWaterValve",
"wait5Seconds",
"closeWaterValve"
};
const int startButton = 2;
const byte pressed = LOW;
const byte released = HIGH;
const byte beamBroken = LOW;
const int sensewaterstn = 3; // Sensor pin (IR sensor, water station)
const int sensedrop = 4; // Sensor pin (IR sensor, bucket drop)
const int alarm = 6; // Alarm condition (light stalk) red wire / relay 7
const int drive = 7; // Motor pin (for conveyor belt stop/start) orange wire / relay 6
const int hold = 8; // Stop plates for bucket pile / relay 5
const int pawl = 9; // Air valve pin (bucket stack pawls) / relay 4
const int gate = 10; // Air valve pin (Bucket exit gate) / relay 3
const int waterfill = 11; // Water valve pin (water fill station) / relay 1
const int jets = 12; // Air valve pin (air jets) / relay 2
// delay code
unsigned long DELAY_TIME5 = 5000; // 5 second delay
unsigned long DELAY_TIME35 = 3500; // 3.5 second delay
unsigned long DELAY_TIME3 = 3000; // 3 second delay
unsigned long DELAY_TIME1 = 1000; // 1 second delay
unsigned long previousWaterstn = 0; // stores last time bucket present at water station
unsigned long previousDrop = 0; // stores last time bucket detected at drop location
void setup() {
// Initialize serial communication
Serial.begin(115200);
Serial.print("Setup-Start");
// Initialize sensor pins as input
pinMode(sensedrop, INPUT_PULLUP);
pinMode(sensewaterstn, INPUT_PULLUP);
digitalWrite(drive, LOW);
digitalWrite(gate, HIGH);
digitalWrite(hold, HIGH);
digitalWrite(pawl, LOW);
digitalWrite(jets, LOW);
digitalWrite(waterfill, LOW);
// Initialize output pins as outputs
pinMode(drive, OUTPUT); // set pins to output
pinMode(gate, OUTPUT);
pinMode(hold, OUTPUT);
pinMode(pawl, OUTPUT);
pinMode(jets, OUTPUT);
pinMode(waterfill, OUTPUT);
pinMode(startButton, INPUT_PULLUP);
printIfStateHasChanged(myStateVar);
}
void loop() {
myStateMachine();
printIfStateHasChanged(myStateVar);
}
void myStateMachine() {
switch (myStateVar) {
case waitForPressingStart: // only in case myStateVar == waitForPressingStart
if (digitalRead(startButton) == pressed) {
myStateVar = moveConveyor;
}
break; // skip other cases and IMMIDIATELY jump down to END-OF-SWITCH
case moveConveyor: // only in case myStateVar == moveConveyor
digitalWrite(drive, HIGH);
myStateVar = checkForBucketInPosition;
break; // skip other cases and IMMIDIATELY jump down to END-OF-SWITCH
case checkForBucketInPosition: // only in case myStateVar == checkForBucketInPosition
if ( digitalRead(sensewaterstn) == beamBroken) {
digitalWrite(drive, LOW); // stop conveyor
myStateVar = openWaterValve;
}
break; // skip other cases and IMMIDIATELY jump down to END-OF-SWITCH
case openWaterValve: // only in case myStateVar == openWaterValve
digitalWrite(waterfill, HIGH); // open water-value which means waterfilling starts
WaterFillTimer = millis(); // store snapshot of time in the moment when waterfilling starts
myStateVar = wait5Seconds;
break; // skip other cases and IMMIDIATELY jump down to END-OF-SWITCH
case wait5Seconds: // only in case myStateVar == wait5Seconds
// check if five seconds have passed by since water-valve opened
if ( TimePeriodIsOver(WaterFillTimer, 5000) ) {
//when REALLY five seconds have passed by since water-valve opened
myStateVar = closeWaterValve;
}
break; // skip other cases and IMMIDIATELY jump down to END-OF-SWITCH
case closeWaterValve: // only in case myStateVar == closeWaterValve
digitalWrite(waterfill, LOW);
myStateVar = waitForPressingStart;
break; // skip other cases and IMMIDIATELY jump down to END-OF-SWITCH
} // END-OF-SWITCH
}
void printIfStateHasChanged(byte p_actualState) {
static byte lastState;
// check if the state has changed since last printing
if (lastState != p_actualState) {
Serial.print("state changed from ");
Serial.print(stateNames[lastState]);
Serial.print(" to ");
Serial.println(stateNames[p_actualState]);
lastState = p_actualState; // update variable lastState
}
}
void formerContentOfLoop() {
// stored into this function to have it with syntax-highlighting available
// looks better with syntax-high-lighting and is easier to read
unsigned long currentWaterstn = millis(); //save current time for water station
unsigned long currentDrop = millis(); //save current time for bucket drop
//Water station
if (digitalRead(sensewaterstn) == LOW) { //bucket present at water station and waiting for fill
digitalWrite(drive, LOW); //turn off conveyor
digitalWrite(waterfill, HIGH); //open fill valve - trigger off-delay relay (analog manual adjust OFF delay simplifies operation)
// check if delay has timed out for initiating water valve
if (currentWaterstn - previousWaterstn >= DELAY_TIME1) { // 1 second delay timer
previousWaterstn = currentWaterstn; // this saves when the water valve last initiated
digitalWrite(waterfill, LOW); // turn OFF water - analog OFF delay relay will continue to dose water till done
} else {
digitalWrite(waterfill, HIGH); //leave water valve ON
}
// check if delay has timed out water valve to finish filling
if (currentWaterstn - previousWaterstn >= DELAY_TIME5) { // 5 second delay timer (maximum time alotted for analog delay timer to fill bucket)
previousWaterstn = currentWaterstn; //copy current time to memory
digitalWrite(waterfill, LOW); // JUST IN CASE - turn OFF water - 5 second overall alotment of time for fill
digitalWrite(drive, HIGH); //turn conveyor back on
}
} else {
digitalWrite(waterfill, LOW); //turn off water
digitalWrite(drive, LOW); //turn off conveyor
}
// Bucket drop detection
if (digitalRead(sensedrop) == HIGH) { //no bucket present
digitalWrite(gate, HIGH); //close exit gate
digitalWrite(pawl, HIGH); //engage pawls to hold stack of buckets
//delay
if (currentDrop - previousDrop >= DELAY_TIME1) { // 1 second delay timer togive time for pawls to engage before opening bottom holding tabs
previousDrop = currentDrop; // this saves when bucket drop last initiated
digitalWrite(hold, LOW); // retract holding tabs - freeing last bucket for drop
} else {
digitalWrite(hold, HIGH); //leave holding tabs engaged - this is to give pawls time to deploy and prevent entire bucket pile from falling
}
//delay
if (currentDrop - previousDrop >= DELAY_TIME3) { // 3 second delay timer
previousDrop = currentDrop; // this saves when bucket drop last initiated
digitalWrite(jets, LOW); // stop air jets
digitalWrite(hold, HIGH); //re-engage holding tabs under bucket pile
} else {
digitalWrite(jets, HIGH); //leave air jets on to give time for bucket dislodging
digitalWrite(hold, HIGH); //re-engage holding tabs - again for insurance
}
//delay
if (currentDrop - previousDrop >= DELAY_TIME35) { // 3.5 second delay timer
previousDrop = currentDrop; // this saves when bucket drop last initiated
digitalWrite(jets, LOW); // stop air jets
digitalWrite(pawl, LOW); //dis-engage pawls from bucket pile
} else {
digitalWrite(pawl, HIGH); //leave pawls engaged to give time for holding tabs to engage first
}
}
}
// easy to use helper-function for non-blocking timing
// explanation see here
// https://forum.arduino.cc/t/example-code-for-timing-based-on-millis-easier-to-understand-through-the-use-of-example-numbers-avoiding-delay/974017
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
unsigned long currentMillis = millis();
if ( currentMillis - startOfPeriod >= TimePeriod ) {
// more time than TimePeriod has elapsed since last time if-condition was true
startOfPeriod = currentMillis; // a new period starts right here so set new starttime
return true;
}
else return false; // actual TimePeriod is NOT yet over
}
.....start
.........drive
.........conveyor
waterstn
.........gate
.........pawl
.........hold
.........jets
.........waterfill
....drop