Hallo

Bild hier  

Aus Zeitgründen habe ich es leider nicht geschafft eine vernünftige Software zu erstellen. Nach Druck auf den linken Bumper wird das hellste Teelicht im Blickbereich angesteuert und beschossen, ein Druck auf den rechten Bumper wiederholt den Schuss. Mein Konzept funktioniert nur im Halbdunkel, aber bei strahlender Sonne braucht man eh keine Teelichter;)

Sensor ist die Projekt-Cam, Ausbläser ist ein Luftballon auf einem PET-Flaschenrest der mit einer Wäscheklammer und zwei Servos gespannt und abgeschossen wird:

Bild hier   Bild hier   Bild hier  
http://www.youtube.com/watch?v=qgmKWWT4lps
http://www.youtube.com/watch?v=l79jabBqgo0


Helferleinkatze ist natürlich auch mit dabei ;)

Code:
// Teelicht löschen mit RP6                                              12.4.2010 mic

#include "RP6RobotBaseLib.h"

#define auf 30
#define zu  12
#define aus 0
#define spannen 14
#define holen   38

uint8_t klammer=0, ziehen=0;	// Servos  aus

void init(void);
void setMotorPWM(uint8_t power_links, uint8_t power_rechts);
uint16_t richtung(void);
void schuss(void);

int main(void)
{
	init();
	while(1)
	{
		writeInteger(richtung(), 10);
		writeString_P("\n");
		setLEDs(63); // ready
		do
		{
			schuss();
			while(!(getBumperRight() || getBumperLeft())); // weiteren Schuss auslösen
		}
		while(!getBumperLeft()); // nächste Kerze
	}
	return(0);
}
ISR (TIMER1_OVF_vect)
{
   static uint16_t servocount=1;
   if(servocount > klammer) PORTC &= ~SCL; else PORTC |= SCL;            // PC0 XBUS 10
   if(servocount > ziehen) PORTC &= ~SDA; else PORTC |= SDA;             // PC1 XBUS 12
   if(servocount < 400) servocount++; else servocount=1;
}
void init(void)
{
   initRobotBase();
   DDRC |= (SCL | SDA);         // Servopins auf Ausgang setzen
   TIMSK |= (1 << TOIE1);       // Die Timer1 Overflow-ISR zur Servoansteuerung

	extIntOFF(); // schaltet den E_INT1-Port auf Eingang für den ADC
// ADC interne Referenz 2,56V, Ergebniss linksbündig, Kanal ADC4 (E_INT1)
	ADMUX = (1<<REFS1) | (1<<REFS0)  | (1<<ADLAR) | 4;
// setzte free running triggern
	SFIOR = (0<<ADTS2) | (0<<ADTS1) | (0<<ADTS0);
// kein interupt, Wandler einschalten, prescaller /2
	ADCSRA = (0<<ADIE) | (1<<ADEN) | (0<<ADPS2) | (0<<ADPS1)  | (1<<ADPS0);
// Autotriggern bedeutet jetzt free running aktivieren, altes Flag löschen
	ADCSRA |= (1<<ADATE) | (1<<ADIF);
// Initialisierung starten
	ADCSRA |= (1<<ADSC);
// und noch die wohl eher unnötige Initiallesung
	while (!(ADCSRA & (1<<ADIF)));
	ADCSRA |= (1<<ADIF);
	//powerON();
}
// Achtung! Die PWM-Werte werden hier OHNE Rampe verändert!
void setMotorPWM(uint8_t power_links, uint8_t power_rechts)
{
extern uint8_t mleft_ptmp, mright_ptmp;

	if(power_links > 210) power_links = 210;
	if(power_rechts > 210) power_rechts = 210;
	mleft_power=mleft_ptmp=power_links;
	mright_power=mright_ptmp=power_rechts;

	OCR1BL = power_links;
	OCR1AL = power_rechts;

	if(power_links || power_rechts)
		TCCR1A = (1 << WGM11) | (1 << COM1A1) | (1 << COM1B1);
	else
		TCCR1A = 0;
}
uint16_t richtung(void)
{
	uint8_t bildspeicher[100], *bildzeiger; // 100 Byte Bildspeicher sollten reichen
	uint8_t zeile, sync, c;                 // und dürfen NICHT global sein!
	uint16_t spalte_max, spaltenwerte[12];

	writeString_P("-------------------------\n");
	do
	{
		spalte_max=0;
		for(c=0; c<12; c++)
		{
		   setLEDs(c); // working
			bildzeiger=&bildspeicher[0]; // Zeiger auf Start des Bildspeicherbereich
			zeile=c*20+35; // aktuelle Zeile (35 Zeilen sind der Schrott beim Bildstart)

		   cli();
			do // Warten auf langen Syncbereich = Bildstart
			{
				sync=0;
				while (ADCH > 20); // warten solange Bilddaten erkannt werden
				while (ADCH < 30) sync++; // Länge des Sync-Signal zählen
			}while (sync < 40); // größer 40 bedeutet Bildstart

			while(zeile--)
			{
				while (ADCH > 20); // Bilddaten
				while (ADCH < 30); // Sync
			}

			do *bildzeiger=ADCH; while(*bildzeiger++ > 20); // schnelle Zeile einlesen
			sei();

			spaltenwerte[c]=0;
			for(zeile=0; zeile<48; zeile++) // 64 Pixel zusammenzählen
			{
            spaltenwerte[c]+=bildspeicher[zeile];
            setLEDs(zeile);
			}
			if(spaltenwerte[c] > spalte_max) spalte_max=spaltenwerte[c];
		}
		for(c=0; c<12; c++) // größten Spaltenwert suchen
		{
		   if(spaltenwerte[c] == spalte_max) spalte_max=c;
		}
		writeString_P("hellste Spalte: ");
		writeInteger(spalte_max, 10);
		writeString_P("  Spaltenwert: ");
		writeInteger(spaltenwerte[spalte_max], 10);
		writeString_P("\n");

		//if(spaltenwerte[spalte_max] > 3000)
		{
		   if(spalte_max > 6) {setMotorDir(FWD,FWD); setMotorPWM(100,0);}
		   else if(spalte_max <6) {setMotorDir(BWD,FWD); setMotorPWM(100,0);}
		   //mSleep(200);
			//setMotorPWM(0,0);
			//mSleep(500);
			else setMotorPWM(0,0);
		}
	}while(spalte_max !=6);
	return(spaltenwerte[spalte_max]); // Summenhelligkeit der Spalte
}
void schuss(void)
{
	klammer=auf;
	mSleep(500);
	ziehen=holen;
	mSleep(500);
	klammer=zu;
	mSleep(300);
	ziehen=spannen;
	mSleep(500);
	klammer=auf;
	mSleep(200);
	klammer=zu;
	mSleep(200);
	klammer=0;
}
Gruß

mic

[Edit]
Ich hab's zerlegt, weil der Ballon kaputt ging. Der kleine Knubbel war im Ballon als Greifpunkt für die Klammer. Das Sieb stammt aus einem Kaffeepad.