//pin mapping: int solU=PIN_B6; //solenoid for cylinder up int solD=PIN_B5; //solenoid for cylinder down int solL=PIN_B4; //solenoid for drawer left int solR=PIN_B3; //solenoid for drawer right int solS=PIN_B2; //solenoid for shaker motor int switchRUNNING=PIN_C7; //on/off switch int switchAUTO=PIN_C6; //auto/manual switch int btnU=PIN_C5; //button for up int btnD=PIN_C4; //button for down int btnL=PIN_C3; //button for left int btnR=PIN_C2; //button for right int btnS=PIN_C1; //button for shaker int potD=PIN_F2; //potentiometer for the drawer int potM=PIN_F1; //potentiometer for the main cylinder int pressuresens=PIN_F5; //pressure sensor input pin F0 for everybody else int ledP=PIN_E0; //Pressure LED int xledA=PIN_E1; //Status LED unsigned long pressurerelieftime=100; unsigned long delaytime=500; //500ms - half second blink of pressure sensor indicator unsigned long testLEDTime=100;// 100ms- blink for test mode int dRetractionTime = -1; //A vanlue of -1 means that we have not yet recorded a value for dRetractionTime int mRetractionTime = -1; unsigned long ledAStartTime=0; //for blink mode unsigned long prestime=0; //for half-second delay of pressure sensor light boolean automode=false; //automode starts at off boolean running=false; boolean ledPIsLit=false; //ledPIsLit starts at off boolean ledAIsLit=false; //led starts at off int hydraulicTestFreq = 20; //The number of miliseconds between //tests of hydraulic pressure sensors //Used within autoExec long int shakeBegin = 0; //Encapsulates a debounced read of the pressure sensor boolean pressureIsHigh(){ if(digitalRead(pressuresens)==HIGH){ delay(20); if(digitalRead(pressuresens)==HIGH){ return true; } } return false; } boolean maxPotMain(){ if(analogRead(potM)>=1020){ return true; } return false; } void setRunning(){ if(!running){ //if the switch is off, digitalWrite(solU,LOW); //turn all solenoids off digitalWrite(solD,LOW); digitalWrite(solL,LOW); digitalWrite(solR,LOW); digitalWrite(solS,LOW);} if(digitalRead(switchRUNNING)==LOW){ //if we are reading the ON switch to indeed be on delay(5); // then delay for debounce if(digitalRead(switchRUNNING)==LOW){ //if it's still low, then we set ON to be true running=true;}} else{ //otherwise, if the on switch is set to off, delay(5); if(digitalRead(switchRUNNING)==HIGH){running=false;} } } void setAuto(){ if(digitalRead(switchAUTO)==HIGH && running){ delay(3); //debounce if(digitalRead(switchAUTO)==HIGH){ //Set the automode global flag. Indicates to the rest of the program that automode is active //Because we're turning on auto, we need to make sure that we have values for dRetractionTime and mExtention time //If we don't have values for either, we need to call drawer bounce //This is because we need the shaft to be open so as not to cause pre-mature compression //INTERUPTS THE MAIN LOOP if(dRetractionTime==-1 || mRetractionTime==-1){ drawerBounce(); } //If we don't have a value for mRetractionTime,l run mainbounce to derive one //INTERUPTS THE MAIN LOOP if(mRetractionTime == -1){ mainBounce(); } automode=true; } } //If the autoswitch is not on, make sure that automode flag is off else if(digitalRead(switchAUTO)==LOW){ delay(3); if(automode && digitalRead(switchAUTO)==LOW){ //Turn off automode and handle variables cleanly automode=false; terminateAutoExec(); } } } void actButtons(){ //this is the function for controlling the machine manually via buttons if(!running) return; //if we ended up here somehow when the on switch is low, go back to where we came from if(digitalRead(btnU)==LOW){ //if we read up button on, wait 3ms for debounce delay(3); if(digitalRead(btnU)==LOW){ //if we read it low still, go to switchsol case 0 and set it high digitalWrite(solU,HIGH); } }else{ delay(3); if(digitalRead(btnU)==HIGH){ //if after debounce it is high, go to switch case 0 and set it low digitalWrite(solU,LOW); } } if(digitalRead(btnD)==LOW){ //if down button is presssed, debounce dat ish delay(3); if(digitalRead(btnD)==LOW){ //still low? ok, drive dat puppy digitalWrite(solD,HIGH); } }else{ //otherwise turn off the solenoid and check the next button delay(3); if(digitalRead(btnD)==HIGH){ digitalWrite(solD,LOW); } } if(digitalRead(btnL)==LOW){ //if left button is low, debounce it delay(3); if(digitalRead(btnL)==LOW){ //still low? turn on solenoid digitalWrite(solL,HIGH); } }else{ //otherwise turn off solenoid and move on to right button delay(3); if(digitalRead(btnL)==HIGH){ digitalWrite(solL,LOW); } } if(digitalRead(btnR)==LOW){ delay(3); if(digitalRead(btnR)==LOW){ digitalWrite(solR,HIGH); } }else{ delay(3); if(digitalRead(btnR)==HIGH){ digitalWrite(solR,LOW); } } if(digitalRead(btnS)==LOW){ //is the shaker motor button pressed? delay(3); if(digitalRead(btnS)==LOW){ // digitalWrite(solS,HIGH); } }else{ delay(3); if(digitalRead(btnS)==HIGH){ digitalWrite(solS,LOW); } } } /////////TEST FUNCTIONS///////////// ///DRAWER CYLINDER TESTS/// void drawerBounce(){ // This function sends the drawer to its forward-most point, then returns to its back limit. // We will change direction and halt whenever we reach a threshold of pressure indicated by our // 'pressuresens' register going HIGH. digitalWrite(solL,HIGH); //Begin extension pressure while(!pressureIsHigh()){ //While we have not reached threshold pressure we have not reached delay(50); //continue to push the drawer } digitalWrite(solL,LOW); //Cut extention pressure pressure digitalWrite(solR,HIGH); delay(100); digitalWrite(solR,LOW); delay(300); digitalWrite(solR,HIGH); //Begin backwards pressure long int retractionStart = millis(); //Record the starttime of our retraction while(!pressureIsHigh()){ delay(300); } dRetractionTime = millis() - retractionStart; digitalWrite(solR,LOW); digitalWrite(solL,HIGH); //for backing off high pressure delay(100); digitalWrite(solL,LOW); return; } float dHaltTime(){ //This function calculates the necessary halt time from the value read by our potentiometer potD //read in the value of the drawer potentiometer float potDValue = analogRead(potD); //Calculate the fraction that the potentiometer has been turned as a number beween 0 and 1.0 float fracTurn = (potDValue / 1023.0); float dTimeScaler = ( ( ((potDValue)/512) - 1 ) * (dRetractionTime / 4) ); //this did not work unless i put +1 after potDValue Serial.println(dTimeScaler,DEC); return (float)((dRetractionTime/2)+ dTimeScaler); } void drawerTiming(){ // This test function sets the drawer cylinder to a position configured with a potentiometer // It is used to calibrate the appropriate halt time as follows if(dRetractionTime==-1){ //If we do not yet have a value for retracionTime, call drawerBounce to derive one drawerBounce(); } delay(50); //Debounce Serial.print("dRetractionTime");Serial.println(dRetractionTime,DEC); Serial.print("dHaltTime");Serial.println(dHaltTime(),DEC); // extend the cylinder until we reach threshold pressure digitalWrite(solL,HIGH); //Begin fowards pressure while(!pressureIsHigh()){ //While we are not at threshold pressure... delay(hydraulicTestFreq); //continue to push the drawer } digitalWrite(solL,LOW); //Cut forward pressure digitalWrite(solR,HIGH); delay(pressurerelieftime); digitalWrite(solR,LOW); // calculate haltTime the amount of time it would take to retract perc percent of the way down the shaft float haltTime = dHaltTime(); // start timer long int timerStart = millis(); long int timeElapsed = 0; //This will represent the elapsed time since beginning of retraction //Debounce delay(50); // retract until timeElapsed has reached haltTime digitalWrite(solR,HIGH); //Begin retraction pressure while(timeElapsed < haltTime && !pressureIsHigh()){ //Continue retracting until we hit our halting time or pressure threshold delay(hydraulicTestFreq); timeElapsed = millis() - timerStart; } digitalWrite(solR,LOW); // The cylinder should now be in position, stop here return; } ///MAIN CYLINDER TESTS void mainBounce(){ // This function sends the drawer to its most retracted point, then returns to its upper limit. // We will change direction and halt whenever we reach a threshold of pressure indicated by our // 'pressuresens' register going LOW. digitalWrite(solU,HIGH); //Begin extension pressure while(!pressureIsHigh()){ //While we have not reached threshold pressure we have not reached delay(hydraulicTestFreq); //continue to push the drawer } digitalWrite(solU,LOW); //Cut exension pressure delay(500); digitalWrite(solD,HIGH); //Begin retraction pressure long int extensionStart = millis(); //Record the starttime of our extension while(!pressureIsHigh()){ delay(hydraulicTestFreq); } mRetractionTime = millis() - extensionStart; digitalWrite(solD,LOW); digitalWrite(solU,HIGH); //for pressure relief delay(100); digitalWrite(solU,LOW); return; } int mHaltTime(){ //This function calculates the necessary halt time from the value read by our potentiometer potD //read in the value of the drawer potentiometer int potMValue = analogRead(potM); //Calculate the fraction that the potentiometer has been turned as a number beween 0 and 1.0 float fracTurn = (potMValue / 1023.0); return (int)(fracTurn * mRetractionTime); } void mainTiming(){ // This test function sets the main cylinder to a position configured with a potentiometer // It is used to calibrate the appropriate halt time as follows if(mRetractionTime==-1){ //If we do not yet have a value for retracionTime, call mainBounce to derive one mainBounce(); } delay(50); //Debounce, don't want to read pressureIsHigh twice in one strike! // retract the cylinder until we reach threshold pressure digitalWrite(solU,HIGH); //Begin extension pressure while(!pressureIsHigh()){ //While we are not at threshold pressure... delay(hydraulicTestFreq); //continue to push the drawer } digitalWrite(solU,LOW); //Cut extension pressure digitalWrite(solD, HIGH); delay(50); digitalWrite(solD,LOW); // calculate haltTime the amount of time it would take to retract perc percent of the way down the shaft float haltTime = mHaltTime(); // start timer long int timerStart = millis(); long int timeElapsed = 0; //This will represent the elapsed time since beginning of retraction //Debounce delay(50); // retract until timeElapsed has reached haltTime digitalWrite(solD,HIGH); //Begin retraction pressure while(timeElapsed < haltTime && !pressureIsHigh()){ //Continue retracting until we hit our halting time delay(hydraulicTestFreq); timeElapsed = millis() - timerStart; } digitalWrite(solD,LOW); // The cylinder should now be in position, stop here return; } void testButtons(){ //this is the function for doing test procedures if(running || automode) return; //if we ended up here somehow when the running switch or automode switch are enabled, get out if(digitalRead(btnU)==LOW){ //if we read up button on, wait 3ms for debounce delay(3); if(digitalRead(btnU)==LOW){ //if we read it low still, then button is pressed, run drawer bounce routine drawerBounce(); } } if(digitalRead(btnD)==LOW){ delay(3); if(digitalRead(btnD)==LOW){ drawerTiming(); } } if(digitalRead(btnL)==LOW){ //if left button is low, debounce it delay(3); if(digitalRead(btnL)==LOW){ //still low? turn on solenoid mainBounce(); } } if(digitalRead(btnR)==LOW){ delay(3); if(digitalRead(btnR)==LOW){ mainTiming(); } } } ////////AUTO STATE MACHINE//////// //The following is a state machine to automate the brick pressing process //TODO: Consider revising to enumerated type const short PUSHBRICK_SHAKE= 0; //Extends the drawer to eject the brick. const short DROP_MAIN=1; const short CLOSECHAMBER_SHAKE = 2; //Moves the drawer to block shaft and allow a surface for compression const short COMPRESS_BRICK = 3; //Extends the main cylinder to compress a brick const short OPENCHAMBER_SHAKE = 4; //Retract the drawer to open the passage between the hopper and compression chamber const short RAISE_BRICK = 5; //Brings the brick to the drawer level //The state we track short autoState = PUSHBRICK_SHAKE; unsigned long lastStateChange = 0; //Marks the last time state was changed boolean stateIsSetup = false; void changeAutoState(int nextState){ autoState = nextState; stateIsSetup = false; //This is used by every state in the execAuto machine //to indicate that the next state must run its setup procedure lastStateChange = millis(); //This marks the time at which states were changed } void turnAllSolenoidsOff(){ digitalWrite(solD, LOW); digitalWrite(solU, LOW); digitalWrite(solR, LOW); digitalWrite(solL, LOW); digitalWrite(solS, LOW); } //This function should be run when autoExec is abrubtly ended. //Because we have many state variables void terminateAutoExec() {changeAutoState(PUSHBRICK_SHAKE); // SO that we always start at state 0 turnAllSolenoidsOff(); } void autoExec(){ //This function is essentially a state machine, performing a single incremental action //every loop cycle depending on the state of autoState //This state machine should not be operating unless the machine is active and on automode if(!running || !automode){return;} //Clear the drawer and open the chamber if(autoState==PUSHBRICK_SHAKE) {if(!stateIsSetup) { digitalWrite(solL,HIGH); digitalWrite(solS,HIGH); delay(50); stateIsSetup = true; } if (pressureIsHigh()) //added by MJ {digitalWrite(solL,LOW); //added by MJ digitalWrite(solS,LOW); //added by MJ //pressure relief: digitalWrite(solR,HIGH); delay(pressurerelieftime); digitalWrite(solR,LOW); //set next state in the changeAutoState loop changeAutoState(DROP_MAIN); } } else if(autoState==DROP_MAIN) //Added by MJ. {if(!stateIsSetup) digitalWrite(solD,HIGH); stateIsSetup = true; if(maxPotMain()==true) { delay(300);//for avoiding geting stuck at the main full extent if(pressureIsHigh()) { digitalWrite(solD,LOW); digitalWrite(solU,HIGH); delay(pressurerelieftime); digitalWrite(solU,LOW); changeAutoState(CLOSECHAMBER_SHAKE); } } else if(maxPotMain()==false) { if (millis() - lastStateChange > mHaltTime()){ digitalWrite(solD,LOW); changeAutoState(CLOSECHAMBER_SHAKE); } } } else if(autoState==CLOSECHAMBER_SHAKE){ //Begin retraction if it has not yet begun if(!stateIsSetup){ digitalWrite(solR,HIGH); digitalWrite(solS,HIGH); stateIsSetup=true; } //If we've reached threshold time... else if(millis() - lastStateChange > dHaltTime()){ //Stop retraction and change states after reaching threshold digitalWrite(solR,LOW); digitalWrite(solS,LOW); changeAutoState(COMPRESS_BRICK); } } //Pushes main cylinder to a compression state against the drawer else if(autoState==COMPRESS_BRICK){ if(!stateIsSetup){ digitalWrite(solU,HIGH); stateIsSetup=true; } else if(pressureIsHigh()){ //Turn off the main cylinder digitalWrite(solU,LOW); //And pull back slightly to relieve pressure digitalWrite(solD, HIGH); delay(pressurerelieftime); digitalWrite(solD,LOW); changeAutoState(OPENCHAMBER_SHAKE); } } else if(autoState==OPENCHAMBER_SHAKE){ if(!stateIsSetup){ digitalWrite(solR,HIGH); digitalWrite(solS,HIGH); stateIsSetup = true; } else if(pressureIsHigh()){ digitalWrite(solR,LOW); digitalWrite(solS,LOW); digitalWrite(solL,HIGH); delay(pressurerelieftime); digitalWrite(solL,LOW); changeAutoState(RAISE_BRICK); } } else if(autoState==RAISE_BRICK){ if(!stateIsSetup){ digitalWrite(solU,HIGH); stateIsSetup = true; } else if(pressureIsHigh()){ digitalWrite(solU,LOW); digitalWrite(solL,HIGH); delay(pressurerelieftime); digitalWrite(solL,LOW); Serial.println("Cycle Complete"); changeAutoState(PUSHBRICK_SHAKE); } } } void ledStatus(){ //This block makes sure that if we're reading high pressure, ledP is lit if(pressureIsHigh() && !ledPIsLit){ //If we read high pressure, but ledP is not lit //...then turn on ledP digitalWrite(ledP,HIGH); //set ledPIsLit flag so as not to run this block redundently ledPIsLit=true; //Record the time we lit up ledP prestime=millis(); } else{ //If we're no longer reading high pressure and enough time has passed for the user to see the LED if(!pressureIsHigh() ){ //&& ((millis()-prestime)>delaytime)){ //Then change flag accordingly ledPIsLit=false; //And turn it off digitalWrite(ledP,LOW); } } //Status LED Setup //automode blink setup if(automode && running){ if(millis()-ledAStartTime>delaytime){ //Turn ledA on if it is off, off if it's on if(ledAIsLit){ digitalWrite(xledA,LOW); ledAIsLit=0; } else{ digitalWrite(xledA,HIGH); ledAIsLit=1; ledAStartTime=millis(); //Either way, reset the clock } } } else if(!automode && !running){ if(millis()-ledAStartTime>testLEDTime){ //Turn ledA on if it is off, off if it's on if(ledAIsLit){ digitalWrite(xledA,LOW); ledAIsLit=0; } else{ digitalWrite(xledA,HIGH); ledAIsLit=1; ledAStartTime=millis(); //Either way, reset the clock } } } else if(!automode && running){ digitalWrite(xledA, HIGH); ledAIsLit=1; return; } else if(automode && !running){ digitalWrite(xledA, LOW); ledAIsLit=0; return; } } // the setup routine runs once when you press reset: void setup() { // initialize the digital pin as an output. pinMode(ledP, OUTPUT); pinMode(xledA, OUTPUT); pinMode(solU, OUTPUT); pinMode(solD, OUTPUT); pinMode(solL, OUTPUT); pinMode(solR, OUTPUT); pinMode(solS, OUTPUT); //initializing LED modes pinMode(ledP, OUTPUT); pinMode(xledA, OUTPUT); //initializing inputs pinMode(btnU, INPUT); pinMode(btnD, INPUT); pinMode(btnL, INPUT); pinMode(btnR, INPUT); pinMode(btnS, INPUT); pinMode(pressuresens, INPUT); pinMode(switchRUNNING, INPUT); pinMode(switchAUTO, INPUT); //We must do this to get accurate reads digitalWrite(btnU, HIGH); digitalWrite(btnD, HIGH); digitalWrite(btnL, HIGH); digitalWrite(btnR, HIGH); digitalWrite(btnS, HIGH); digitalWrite(pressuresens, HIGH); digitalWrite(switchRUNNING, HIGH); digitalWrite(switchAUTO, HIGH); pinMode(potM, INPUT); pinMode(potD, INPUT); Serial.begin(9600); //For debugging } // the loop routine runs over and over again forever: void loop() {ledStatus(); setRunning(); setAuto(); if(automode){ autoExec();} else{ if(running){ actButtons();} else{ testButtons();} } //Serial.println(pressureIsHigh()); // if(pressureIsHigh()){ // Serial.println("HIGH PRESSURE READ");} // Serial.print("potM");Serial.println(analogRead(potM),DEC); }