Change between two loops when a momentary switch is pressed

Evening guys and gals :slight_smile:
I've been working on a project recently which uses Steve Hobley's Pixart IR Tracking program to control a pan servo to follow an IR blob (code below) However, I would like to be able to use manual controls as well via an analogue joystick (code below). The joystick has a push button, so I was wondering if I could jump between the tracking bit of the code and the manual control bit of the code by pressing and releasing the momentary switch?
Tracking code:
// Sweep
// by BARRAGAN http://barraganstudio.com

#include <Servo.h>
#include <Wire.h>
#include <PVision.h>

Servo myservo2; // create servo object to control a servo
// a maximum of eight servo objects can be created

PVision ircam;

byte result;
int ledPin = 11; // LED connected to digital pin 13
int pos = 0; // variable to store the servo position

void setup()
{
myservo2.attach(6); // attaches the servo on pin 9 to the servo object
Serial.begin(9600);
ircam.init();
}

void loop()
{

result = ircam.read();

if (result & BLOB1)
{

Serial.print("BLOB1 detected. X:");
Serial.print(ircam.Blob1.X);
Serial.print(" Y:");
Serial.print(ircam.Blob1.Y);
Serial.print(" Size:");
Serial.println(ircam.Blob1.Size);
pos = ircam.Blob1.X / 5;
myservo2.write(pos);
}

if (result & BLOB2)
{
digitalWrite(ledPin, HIGH); // sets the LED on
Serial.print("BLOB2 detected. X:");
Serial.print(ircam.Blob2.X);
Serial.print(" Y:");
Serial.print(ircam.Blob2.Y);
Serial.print(" Size:");
Serial.println(ircam.Blob2.Size);
}
if (result & BLOB3)
{
Serial.print("BLOB3 detected. X:");
Serial.print(ircam.Blob3.X);
Serial.print(" Y:");
Serial.print(ircam.Blob3.Y);
Serial.print(" Size:");
Serial.println(ircam.Blob3.Size);
}
if (result & BLOB4)
{
Serial.print("BLOB4 detected. X:");
Serial.print(ircam.Blob4.X);
Serial.print(" Y:");
Serial.print(ircam.Blob4.Y);
Serial.print(" Size:");
Serial.println(ircam.Blob4.Size);
}
digitalWrite(ledPin, LOW); // sets the LED on

}
Manual controls:

#include <Servo.h>

Servo panServo;
Servo tiltServo;

int servoPanPosition = 90;
int servoTiltPosition = 90;
int joystickPanPin = A1;
int joystickTiltPin = A0;
int joystickButton = 2;
int joystickPanSpeed = 0;
int joystickTiltSpeed = 0;
int servoPanPin = 6;
int servoTiltPin = 5;
int orangeLED = 10;
int greenLED = 11;
int brightness = 0;
int fadeAmount = 5;
int checkMovement = 0;
void setup()
{
Serial.begin(9600);
pinMode(servoPanPin, OUTPUT);
pinMode(servoTiltPin, OUTPUT);
panServo.attach(servoPanPin);
tiltServo.attach(servoTiltPin);
pinMode(orangeLED, OUTPUT);
pinMode(greenLED, OUTPUT);
}

void loop()
{
joystickPanSpeed = (analogRead(joystickPanPin) - 512) / 150;
joystickTiltSpeed = (analogRead(joystickTiltPin) - 512) / 150;

servoPanPosition = constrain((servoPanPosition + joystickPanSpeed), 1, 170);
servoTiltPosition = constrain((servoTiltPosition + joystickTiltSpeed), 63, 130);

panServo.write(servoPanPosition);
tiltServo.write(servoTiltPosition);

checkMovement = (joystickPanSpeed + joystickTiltSpeed);
if (checkMovement != 0) {
digitalWrite(greenLED, HIGH);
}
checkMovement = (joystickPanSpeed + joystickTiltSpeed);
if (checkMovement = 0) {
digitalWrite(greenLED, LOW);
}

//analogWrite(greenLED, brightness);
//brightness = joystickPanSpeed;
//if (brightness>0) {
//brightness = -joystickPanSpeed ;
//}
digitalWrite(orangeLED, HIGH);
Serial.print(" greenLED "); Serial.print( int(brightness));
Serial.print(" servoTilt "); Serial.print( int(servoTiltPosition ));
Serial.print(" checkMovement "); Serial.print( int(checkMovement ));
Serial.println("");
delay(10);
}

You'll have to first integrate the two sketches to remove pin clashes, duplicate function names etc.

And please use code tags when posting code.

I don't think you can have two loops, but if those loops you have are changed to subroutines, you simply have another all-new loop to poll and respond to the buttons etc and determine which subroutine is used.

Ok, thanks for the quick responses, so once I integrate the sketches etc, how to I get the button push to jump between functions?
I want to achieve something similar to what is happening in this video;

Thanks :slight_smile:

Well, you could have the button trigger an interrupt.
In the interrupt service routine, you toggle a flag.

If your code, you test the flag

loop() {
  . . .
  if (flag == true) {
    // do all the part 1 stuff as before
  }
  else {
    // do all the part 2 stuff as before
  }
  . . .
  . . .
}

6v6gt:
Well, you could have the button trigger an interrupt.

Very, very bad advice!

Paul__B:
Very, very bad advice!

That is the beauty of programming. Even following very bad advice, you are able to construct working, efficient and functionally correct programs. Naturally these may lack a little technical elegance but, sometimes, compromises are necessary. :slight_smile:

You can try this code (not tested)

// Sweep
// by BARRAGAN <http://barraganstudio.com> 

#include <Servo.h> 
#include <Wire.h>
#include <PVision.h>

// Servo myservo2;  // create servo object to control a servo 
// a maximum of eight servo objects can be created 

PVision ircam;

byte result;
const int ledPin = 11;   // LED connected to digital pin 13 
int pos = 0;       // variable to store the servo position 


Servo panServo;      
Servo tiltServo;      

int servoPanPosition = 90;
int servoTiltPosition = 90;
const int joystickPanPin = A1;
const int joystickTiltPin = A0;
//const int joystickButton = 2;
int joystickPanSpeed = 0;
int joystickTiltSpeed = 0;
const int servoPanPin = 6;
const int servoTiltPin = 5;
const int orangeLED = 10;
const int greenLED = 11;
int brightness = 0;
int fadeAmount = 5;
int checkMovement = 0;

// constants won't change. They're used here to 
// set pin numbers:
const int joystickButton = 2;    // the number of the pushbutton pin


// Variables will change:
int ManualState = HIGH;         // the current state of the Manual loop
int buttonState;             // the current reading from the input pin
int lastButtonState = LOW;   // the previous reading from the input pin

// 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() 
{ 
	// myservo2.attach(6);  // attaches the servo on pin 6 to the servo object 
	Serial.begin(9600);
	ircam.init();
	
	pinMode(joystickButton, INPUT);
	pinMode(servoPanPin, OUTPUT);
	pinMode(servoTiltPin, OUTPUT);
	panServo.attach(servoPanPin);
	tiltServo.attach(servoTiltPin);
	pinMode(orangeLED, OUTPUT);
	pinMode(greenLED, OUTPUT);
} 


void TrackingLoop() 
{ 

	result = ircam.read();

	if (result & BLOB1)
	{

		Serial.print("BLOB1 detected. X:");
		Serial.print(ircam.Blob1.X);
		Serial.print(" Y:");
		Serial.print(ircam.Blob1.Y);
		Serial.print(" Size:");
		Serial.println(ircam.Blob1.Size);
		pos = ircam.Blob1.X / 5;
		panServo.write(pos);
	}

	if (result & BLOB2)
	{
		digitalWrite(ledPin, HIGH);   // sets the LED on
		Serial.print("BLOB2 detected. X:");
		Serial.print(ircam.Blob2.X);
		Serial.print(" Y:");
		Serial.print(ircam.Blob2.Y);
		Serial.print(" Size:");
		Serial.println(ircam.Blob2.Size);
	}
	if (result & BLOB3)
	{
		Serial.print("BLOB3 detected. X:");
		Serial.print(ircam.Blob3.X);
		Serial.print(" Y:");
		Serial.print(ircam.Blob3.Y);
		Serial.print(" Size:");
		Serial.println(ircam.Blob3.Size);
	}
	if (result & BLOB4)
	{
		Serial.print("BLOB4 detected. X:");
		Serial.print(ircam.Blob4.X);
		Serial.print(" Y:");
		Serial.print(ircam.Blob4.Y);
		Serial.print(" Size:");
		Serial.println(ircam.Blob4.Size);
	}
	digitalWrite(ledPin, LOW);   // sets the LED on
} 

// Manual controls:


void ManualLoop()
{
	joystickPanSpeed = (analogRead(joystickPanPin) - 512) / 150;
	joystickTiltSpeed = (analogRead(joystickTiltPin) - 512) / 150;

	servoPanPosition = constrain((servoPanPosition + joystickPanSpeed), 1, 170);
	servoTiltPosition = constrain((servoTiltPosition + joystickTiltSpeed), 63, 130);

	panServo.write(servoPanPosition);
	tiltServo.write(servoTiltPosition);
	
	checkMovement = (joystickPanSpeed + joystickTiltSpeed);
	if (checkMovement != 0) {
		digitalWrite(greenLED, HIGH);
	}
	checkMovement = (joystickPanSpeed + joystickTiltSpeed);
	if (checkMovement = 0) {
		digitalWrite(greenLED, LOW);
	}


	//analogWrite(greenLED, brightness);
	//brightness = joystickPanSpeed;
	//if (brightness>0) {
	//brightness = -joystickPanSpeed ;
	//}
	digitalWrite(orangeLED, HIGH);
	Serial.print("   greenLED "); Serial.print( int(brightness));
	Serial.print("   servoTilt "); Serial.print( int(servoTiltPosition ));
	Serial.print("   checkMovement "); Serial.print( int(checkMovement ));
	Serial.println("");
	delay(10);
}


void loop()
{
	// read the state of the switch into a local variable:
	int reading = digitalRead(joystickButton);

	// 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 (reading != lastButtonState) {
		// 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 (reading != buttonState) {
			buttonState = reading;

			// only toggle the LED if the new button state is HIGH
			if (buttonState == HIGH) {
				ManualState = !ManualState;
			}
		}
	}

	if (ManualState)
	ManualLoop();
	else
	TrackingLoop();

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

}

6v6gt:
That is the beauty of programming. Even following very bad advice, you are able to construct working, efficient and functionally correct programs. Naturally these may lack a little technical elegance but, sometimes, compromises are necessary. :slight_smile:

You said:

6v6gt:
Well, you could have the button trigger an interrupt.
In the interrupt service routine, you toggle a flag.

So, your pushbutton bounces.

The interrupt is called about a dozen times. Each time it toggles the flag.

If the number of bounces is even (in addition to the initial contact), the flag has toggled when it is eventually read. If the number is odd, then it has not toggled. You call that a "working, efficient and functionally correct program", do you?

Paul__B:
You said:So, your pushbutton bounces.

The interrupt is called about a dozen times. Each time it toggles the flag.

If the number of bounces is even (in addition to the initial contact), the flag has toggled when it is eventually read. If the number is odd, then it has not toggled. You call that a "working, efficient and functionally correct program", do you?

Of course it would work perfectly. Only the newest newbie would fail to include a couple of lines of code in the ISR to reject attempts to toggle the flag, if the last successful attempt was within the last few milliseconds. I can include a code example if required.

6v6gt:
Only the newest newbie would fail to include a couple of lines of code in the ISR to reject attempts to toggle the flag, if the last successful attempt was within the last few milliseconds.

Uh-huh. :roll_eyes:

And here is the code sample, which I have no doubt you could have put together yourself, but am including for the benefit of those who are still green behind the ears.

void ISR_buttonPress () {
  // toggles global boolean flag on button press with debounce  
  static long unsigned lastToggleMillis ;
  if ( millis() - lastToggleMillis > 100  ) {  // 100mS
    flag = ! flag ;
    lastToggleMillis = millis();
  }
}

The 100mS debounce time is for the average user. If the user, however, may be going through cold turkey, DT, or at an advance state of Parkinson's disease, this value could of course be increased accordingly.
Incidentally, I've increased you Karma count in acknowlegement of your valiant (but unsuccessful) attempts to bait me.

6v6gt:
If the user, however, may be going through cold turkey, DT, or at an advance state of Parkinson's disease, this value could of course be increased accordingly.

Or in fact, if the button bounces on release.