Brain fried on sawmill project.(update: working code)

Please bear with me as I’m new to this kind of code. I’ve only been messing around with this arduino and code for about a month now. Frustrating at times but I enjoy the challenge. At the moment though I feel brain fried as I cant find an answer to a few code issues.

The project is automating functions on my sawmill I built, so that a push of a button would do 3 tasks.
I have a laser/photoresistor that is broke when it passes through the log. So the idea is to push the start button which kicks on the drive motor. It will start with the laser contacting, then break the laser as it cuts through the log, and then I would like the drive motor to turn off about 1 second after the laser contacts again. I then wish to drive a motor to raise the sawmill head for 3 seconds, and then kick on the drive motor to return the saw to the beginning until it hits a limit switch ending the circuit until the button is pushed again.

The problem I’m having so far is making all the tasks wait until the momentary start button is pressed. It seems to want to loop through the code, and fire the remaining sequences repeatedly. Also the laser part is tricky, as I need the drive motor to run until the laser is tripped once for however long, and then to stop only after it has reconnected.

This may sound confusing but here’s a video of the mill, so you can see what I’m referring to: http://www.youtube.com/watch?v=KI5bEX-ddzY

I may not have explained what I want good enough, so question away.
Any tips on the code would be appreciated!
UPDATE: New code in later post.

// set pin numbers:
const int buttonPin = 2;     // the number of the pushbutton pin
const int ledPin =  13;      // Drive motor led
const int ledPin2 =  12;      // Head raise led
const int ledPin3 =  11;      // Drive reverse led
const int analogPin = A5;    //  Photorisistor pin. 
const int ledPin4 = 10;       // Led simply showing laser/photoresistor contact.
const int threshold = 800;   // an arbitrary threshold level that's in the range of the analog input


// variables will change:
int buttonState = 0;         // variable for reading the pushbutton status



void setup() {
 
  pinMode(ledPin, OUTPUT);      
  pinMode(ledPin4, OUTPUT);      
  pinMode(ledPin2, OUTPUT);      
  pinMode(ledPin3, OUTPUT);      
  pinMode(buttonPin, INPUT);    
  // initialize serial communications:
  Serial.begin(9600);
  
}

void loop(){
  // read the state of pushbutton 1
  buttonState = digitalRead(buttonPin);
    // start drive motor    
    if (buttonState == HIGH)     
    // start drive motor    
    digitalWrite(ledPin, HIGH);
  // read the value of the photoresistor
  int analogValue = analogRead(analogPin);

  // if the analog value is high enough, turn on the LED:
  if (analogValue < threshold) {
digitalWrite(ledPin, HIGH);
    digitalWrite(ledPin4, LOW);
  } 
  else {
    digitalWrite(ledPin4,HIGH);
   delay(1500); 
  digitalWrite(ledPin, LOW);
 delay(1000);
 digitalWrite(ledPin2, HIGH);
 //raise the head for 3 seconds
  delay(3000);
  // turn LED off:
    digitalWrite(ledPin2, LOW);
    digitalWrite(ledPin3, HIGH);  
  //reverse for 6 seconds, will need to be changed for limit switch. 
  delay(5000);
  // turn reverse motor off, will need to be changed for limit switch. 
    digitalWrite(ledPin3, LOW);
} 

    
    
  }

Please excuse my ignorance with all this!

void loop(){
  // read the state of pushbutton 1
  buttonState = digitalRead(buttonPin);
    // start drive motor    
    if (buttonState == HIGH)     
    // start drive motor    
    {     // <<-----  I think you you missed out this '{' and the corresponding '}' at the end of the program
      digitalWrite(ledPin, HIGH);
      ...

I am surely no expert on the Arduino coding (yet), but I have been writing machine control code for years in other languages. The following two general thoughts might help simplify your code when designing machine control code using the Ard.

  1. Always expect false signals unless you have firmly soldered every connection. This means you should debounce each digital input to make sure it is true. Simple debouncing can be done by forcing 25-50 confirmed reads in a row before accepting the input. This happens really fast inside the Ard.

  2. Learn to use interrupts to handle your digital inputs. I recently completed code to run a filling machine where the loop code got very confusing. Interrupt handlers cleaned the loop code out and made things much easier to follow.

Hope this helps.

Let me add one more point. When trying to convey or write machine control code, it is immeasurably helpful to use "pseudo-code" to make it easier for others to understand. i.e. 0. start main transport drive 1. laser beam broken 2. start main saw drive 3. listen for laser input or limit switch 4. cut main saw drive 5. wait 3 seconds 6. turn on elevator drive. 7. listen for elevator drive limit switch 8. cut elevator drive 9. set reverse on main transport drive 10. start main transport drive 11. listen for home limit switch 12. cut main drive

If this is what you want, it's really simple. Made simpler for me by just writing down the "pseudo code". To keep my events out of the loop code, I would handle them with interrupts them. Don't know which Ard you are using. The Uno has only 2 interrupts, but I think the Mega has more.

DC you were right and I was missing those, which helped isolate the loop to wait for the button.

I've tweaked and changed stuff and it's still acting strange.

jsearles, that's close to right, and I'm using the UNO.

Updated: 0. start main transport drive 1. laser beam broken 2. listen for laser input or limit switch 3. cut main transport drive 4. turn on elevator drive. 5. wait 3 seconds 6. cut elevator drive 7. set reverse on main transport drive 8. start main transport drive 9. listen for home limit switch 10. cut main drive

I'm having the most trouble with the laser interaction part. It seems simple but makes me feel simple minded. :~

I think you're missing a piece at the start - when you turn on the drive, the laser won't be blocked until the blade reaches the log. You're looking for the laser to be visible as a sign that the cut is over, but it'll be on when you start the process so the code will immediately execute the 'back to start' logic.

Another thing that might help is to change the names of the pins to match their function e.g. DriveMotorPin rather than ledpin.

Yet another important tip:

Never, ever, use delay() as a timer. Have a look at millis() and change all those delays with millis() code. Reason for that is that you can't do anything if the delay is running. But the program can keep on running and actually take measures in case something goes wrong. It is a little trickier to write the code, but the end product is far better and possibly safer too.

I was thinking the same thing, but I assume (or I hope) that the emergency stop is a more foolproof, non arduino controlled component

Pyro,

wildbill is right, please change your pin names so they make sense.

Please explain what kind of problem you are having with the laser.

Wildbill, exactly as you said the laser will be connected already, then break, then reconnect. That is part of what I'm having trouble getting right. Any suggestion there? Maybe tell it to wait for 2 change states? Or maybe after the laser has been tripped for more than a couple seconds? I will also take your advice on renaming the pins.

bubulindo, I will change over the delays to millis, I've just ignored that part so far since the beginning of the circuit was giving me trouble.

And yes, the emergency stops will all be wired to the main circuit. If the stop gets hit the power to the motors themselves will be interrupted.

RIght now with the laser connected at start, when I push the button the program immediately turns the drive led on, then off 1.5 seconds later, then runs the up/reverse.

I need it to be connected, start the drive led, trip the laser, then 1.5 seconds after reconnect with laser turn off the drive led, and run up/reverse.

I'll play with it and change some stuff and try to post some new improved code soon.

Thanks so far for the tips guys!

If that was me, I'd tackle this as a finite state machine. Start by defining a sequence of states that the system is expected to go through.

Idle (waiting for the start button) Started (waiting for the beam to be broken) Sawing (waiting for the beam to be unbroken) Stopping saw (waiting for 1 second delay) Raising Saw (waiting for 3 seconds) Returning Saw (waiting for limit switch to be triggered)

Define these values using an enum, or consts, or #defines, or whatever other method you prefer.

Have a state variable which records what state the system is currently in. Decide how the system knows when to leave one state for the next state. There may be abnormal paths such as timing out waiting for the laser beam to be broken, or unbroken, or waiting for the limit switch to be triggered. In each state, decide how you are going to get back to a clean initial state if something goes wrong. (Can you reset the saw manually if necessary?) Poll for events that tell you it is time to leave the current state. At each state transition, stop the things that were supposed to be happening in the old state and start the things that are supposed to be happening in the new state, and resume polling for the events that will trigger the next state transition.

Personally, I’d go with the state machine approach too - clean, elegant, extensible and easy to follow. However, you would need to completely rebuild your sketch. This will do what you need I think:

while(analogRead(analogPin)>threshold)
  ; //Do nothing

Put it in after you turn the drive on. BTW, that mill is pretty cool - how wide were the poplar boards you cut from that piece?

Thanks wildbill, the boards coming off that cut were 16" wide. It’s a 16"x18" cant, and the butt of the log was 26" diameter. Quite a beast.

Ironically at a family dinner I found out my brother knows C, when he rewrote my code while I was busy. I was pissed when I heard but at least it works correctly now. XD

He used while loops to separate the process much better than my attempt.
Current process:

  1. wait for button push.
  2. Start drive motor, and light up led anytime laser is contacted.
  3. when laser is broke for at least 2 seconds, wait for reconnect.
  4. continue drive for 1.5 seconds, then stop drive, pause for 1 second.
  5. raise head 3 seconds.
  6. reverse head for 5 seconds(needs to be switched out for limit switch still).
  7. End process, wait for button press again.

Overall a much cleaner and correctly working code. Since he still used delays I’m guessing I should still switch those out with millis? Also any tips on the improved code?

// set pin numbers:
const int startbutton = 2;     // the number of the pushbutton pin
const int driveforward =  13;      // Drive motor led
const int headraise =  12;      // Head raise led
const int drivereverse =  11;      // Drive reverse led
const int laserreciever = A5;    //  Photorisistor pin. 
const int laserLED = 10;       // Led simply showing laser/photoresistor contact.
const int threshold = 800;   // an arbitrary threshold level that's in the range of the analog input
long previousMillis = 0;
int tripTime = 2; // time to wait for safety (in seconds)


// variables will change:
int buttonState = 0;         // variable for reading the pushbutton status



void setup() {

  pinMode(driveforward, OUTPUT);      
  pinMode(laserLED, OUTPUT);      
  pinMode(headraise, OUTPUT);      
  pinMode(drivereverse, OUTPUT);      
  pinMode(startbutton, INPUT);
  pinMode(laserreciever, INPUT);  
  // initialize serial communications
  Serial.begin(9600);

}

void loop(){

  if (analogRead(laserreciever)<threshold) {
    digitalWrite(laserLED,LOW);
  }
  if (analogRead(laserreciever)>threshold) {
    digitalWrite(laserLED,HIGH);
  }

  // When button is pressed, start the procedure
  buttonState = digitalRead(startbutton);
  if (buttonState == HIGH){    

    // start drive motor    
    digitalWrite(driveforward, HIGH);

    // Wait until photoresistor is tripped for tripTime
    int i=0;
    tripTime = tripTime*10;
    while (i<tripTime){
      if (analogRead(laserreciever)<threshold){
        i++;
        delay(100);  
        digitalWrite(laserLED,LOW);
      } else {
        i=0;
        digitalWrite(laserLED,HIGH);
      }      
    }
    while(analogRead(laserreciever)<threshold);
    tripTime=tripTime/10;
    i=0;
    digitalWrite(laserLED,HIGH);
    delay(1500);


    /*while (analogRead(laserreciever)<threshold){
      digitalWrite(laserLED,LOW);
    }
    while (analogRead(laserreciever)<threshold){
      digitalWrite(laserLED,LOW); 
    }*/
    
    // Turn the laser led back on --- just here for visual consistency
    if (analogRead(laserreciever)>threshold){
      digitalWrite(laserLED,HIGH);
    }

    // Stop the drive motor and raise the head
    digitalWrite(driveforward, LOW);
    delay(1000);
    digitalWrite(headraise, HIGH);
    delay(3000);
    digitalWrite(headraise, LOW);

    //reverse for 5 seconds, will need to be changed for limit switch. 
    digitalWrite(drivereverse, HIGH);  
    delay(5000);
    digitalWrite(drivereverse, LOW);

  } // End cycle (first "if" statement) from pushing button