Agent 00L – Liebesgrüße aus Helsinki

Linux Robotics

01.09.2003
Mein Name ist Baud, James Baud. Meinen Auftraggeber habe ich nie zu Gesicht bekommen; wenn er mit mir Kontakt aufnimmt, nennt er sich nur "Der Pinguin". Für meinen nächsten Job hat mir der Pinguin einen Spybot von LEGO zukommen lassen.

Die Anweisungen des Pinguins waren meistens klar und deutlich, enthielten aber auch immer wieder Überraschungen. Seine letzte Nachricht wies mich an, ein Paket aus einem Schließfach am Hauptbahnhof zu übernehmen. Ich staunte nicht schlecht, als ich einen originalverpackten Spybot-Roboter von LEGO (Agentenfoto 1 zeigt ihn bereits zusammengebaut) in den Händen hielt und die Nachricht des Pinguins dazu las. Sie lautete:

"Agent Baud! Wir brauchen einen Agitator im Gebäudekomplex
meines Widersachers Fredi Fenster. Sein Hauptgebäude ist perfekt
abgesichert. Finden Sie einen Weg hinein für den Spybot. Benutzen Sie
auf keinen Fall Windows. Weitere Informationen und Anweisungen folgen."

Das war mal wieder typisch für den Pinguin: "Auf keinen Fall Windows" gehörte zu seinen Standardanweisungen.

Abbildung 1: Der Spybot

Konstruktionspläne

Bevor ich mich jedoch dem Gebäude des Feindes zuwandte, wollte ich zunächst meinen neuen, kleinen Agentenfreund studieren. Als ich ihn auspackte, hatte ich lediglich den Rumpf, einen Infrarot-Sender und einige Tüten Kleinteile in der Hand. Außerdem enthielt das Paket ein serielles Kabel, an dessen Ende ein Infrarot-Sender / -Empfänger befestigt war (Agentenfoto 2). Der Stecker passte genau in das Heck des Rumpfes – nun gut, die Leitungen für die Verbindung meines Computers zum technischen Helferlein waren gelegt.

Ich wandte mich dem nächsten Problem zu: Die Bauanleitung lag leider nicht in gedruckter Form bei; sie ist der CD-ROM (ausschließlich Software für Windows 98, ME und XP) zu entnehmen. Diesen ersten Stolperstein versuchte ich mit Hilfe des Windows-Emulator wine zu umschiffen. Nach gelungener Installation der Spybotics-Software und des Movieplayers QuickTime keimte Hoffnung in mir auf – zu früh gefreut. Nach dem Start der Software flimmerte das Intro über den Schirm (Agentenfoto 3). Leider hängte sich die LEGO-Software-wine-Kombination bei der Kabelsuche auf (Agentenfoto 4). Auch die unterschiedlichsten Konfigurationen blieben alle erfolglos. Um wine bei der Suche nach dem Roboter zu unterstützen, kam ich auf die Idee, der Software vorzugaukeln, sie hätte ihn schon längst gefunden. Ich kontaktierte einen Freund von mir – er ist Doppelagent (seine Auftraggeber kennen den Standardpassus "auf keinen Fall Windows" nicht unbedingt) und sollte mir eine User.ini-Datei besorgen, die auf den ersten seriellen Port (also /dev/ttyS0) abgestimmt ist. Ich kopierte diese an die entsprechende Stelle meiner wine-Konfiguration. Auch wenn das wine-LEGO-Team so etwas weiter kam, hing das Programm nach kurzer Zeit wieder. Die speziell für Spiele ausgelegte wine-Alternative winex scheiterte erstaunlicherweise noch früher als wine selbst und verweigerte selbst die Installation der Software.

Abbildung 2: IR-Stecker des LEGO-Spybots
Abbildung 3: LEGO-Intro
Abbildung 4: Kabelsuche

Sollte hier bereits mein Auftrag enden, weil ich keine Konstruktionspläne für den kleinen Spion besaß? Ich untersuchte die LEGO-CD-ROM genauer. Glücklicherweise waren die Einzelschritte für den Zusammenbau im Verzeichnis Shared/build/ im JPG-Format abgespeichert. Da die CD-ROM für sämtliche Spybot-Modelle ausgeliefert wird, finden sich hier auch alle Bauanleitungen wieder. Ein Blick auf die Verpackung verriet die Artikelnummer für meinen Spybot Gigamesh G60: Es war die 3806, und tatsächlich hießen alle JPG-Dateien 3806-*.jpg und waren sauber durchnumeriert. Besonders komplizierte Bauschritte unterstützt LEGO zusätzlich mit kleinen Video-Sequenzen. Hier konnte ich das zuvor installierte QuickTime gut gebrauchen. Mit

user@host:~$ wine "C:\Program Files\LEGO Software\Products\QuickTime\QuickTimePlayer.exe 3806-08a.mov"

lässt sich das entsprechende Video problemlos ansehen. So ausgestattet war war es nicht weiter schwierig, meinen technischen Mitverschwörer zusammenzubauen. An der Front besitzt er einen Drucksensor, die Motoren sitzen rechts und links. Pfiffig gelöst hat LEGO die doppelte Nutzung der IR-Schnittstelle. Nimmt man das serielle Kabel ab, so kann die Laserkanone in den IR-Ausgang und der Lichtmesser in den IR-Eingang gesteckt werden.

Ausgeliefert!

Mein Freund der Doppelagent hatte mir jedoch nicht nur die User.ini-Datei beschafft, sondern auch sonst einiges von den Möglichkeiten der LEGO-Software berichtet. So ist sie mit zehn spannenden Missionen für den Spybot ausgestattet, mit denen ich mich wahrscheinlich bestens auf den Auftrag des Pinguins hätte vorbereiten können – zumindestens entnehme ich das den Videos der CD-ROM. Da die Software bei mir aber streikte, blieb mir vorerst nur das Programm, was LEGO als Standard in den Spybot bei Auslieferung reinsteckt. Nachdem ich den Startknopf gedrückt hatte, gingen zuerst einmal die LEDs an und der Roboter fuhr los: Nach einer kurzen Strecke drehte er sich ein wenig, und begann wieder von vorne. Wenn er gegen eine Wand oder ein anderes Hindernis fuhr, löste sein Drucksensor einen "Autsch"-Klang aus.

Nun war es an der Zeit, sich den IR-Controller (Agentenfoto 5) zum Spybot genauer anzusehen. Dieser besitzt drei Modi: Fernbedienung, Link- und Aktionsmodus. Mit den vier Knöpfen auf dem Controller lässt sich der linke und rechte Motor jeweils vor - und zurückkommandieren. Im Aktionsmodus feuert der rote Knopf die Laserkanone des Spybots ab. Mit diesen neuen Erkenntnissen erschien die Erfüllung meines Auftrags für den Pinguin gesichert, und prompt fand ich seine nächste Anweisung am folgenden Tag in meinem Briefkasten:

"Baud! Ein anderer Agent hat herausgefunden, dass die Alarmanlage Fredi
Fensters bei Infrarotlicht auslöst. Sorgen Sie dafür, dass dies in einer
Nacht ständig geschieht, damit mein Gegner an einen Defekt glaubt und
die Anlage abschalten lässt. Wir setzen den nächsten Neumond als
Zieltermin an."
Abbildung 5: Der IR-Controller

Spionprogramme im Eigenbau

Da ich auf die Standardprogramme der LEGO-CD nicht zurückgreifen konnte, intensivierte ich meine geheimen Ermittlungen und wurde schließlich unter [3] fündig. Mit dem Tool NQC ("Not Quite C") lässt sich der Spybot ab Version 2.5r1 unter Linux individuell programmieren. Laden Sie die Sourcen entweder von der Website des Programms (z. B. mit wget http://www.baumfamily.org/nqc/release/nqc-2.5.r1.tgz) herunter oder kopieren Sie die Datei nqc-2.5.r1.tgz von der Heft-CD aus dem Verzeichnis LinuxUser/lego/ nach /usr/local/. Mit dem Befehl

tar -zxvf nqc-2.5.r1.tgz

entpacken Sie das Archiv. Im neu entstandenen Verzeichnis nqc-2.5.r1 ausgeführt sorgen

make

und

make install

für die Installation von NQC. Voraussetzung für einen reibungslosen Ablauf ist die vorherige Installation des Programms bison, welches nicht unbedingt zu einer Standard-Linux-Installation gehört.

NQC

NQC-Programme sind in ihrer Syntax C-Programmen sehr ähnlich. Anweisungen werden durch ein Semikolon abgeschlossen, und Blöcke stehen in geschweiften Klammern { }. Kommentare lassen sich durch /* und */ einfassen oder stehen ab // bis zum Zeilenende. Die Kontrollstrukturen if, while und for entsprechen denen in C. Zusätzlich bietet NQC repeat und until an (Kasten 1). Ähnlich wie bei C muss in NQC ein Hauptprogramm existieren, der Task definiert durch task main() (siehe Listing 1). In diesem (und jedem anderen) Task können die Anweisungen an den Roboter kodiert werden. Eine einfache Anweisung ist beispielsweise OnFwd(OUT_A+OUT_B). Hier steckt eine Menge drin: Zwei Ausgänge, OUT_A und OUT_B (durch ein + verbunden), werden als Parameter übergeben. Die Funktion OnFwd() ist bereits eine Kombination aus On() und Fwd() und damit direkt Richtungsangabe- und Einschaltbefehl zusammen.

Listing 1 zeigt weiterhin die Funktion Wait(). Ihr wird als Parameter die Anzahl Hundertstelsekunden, die der Roboter einfach das weitermachen soll, was er gerade tut, übergeben. OnRev(OUT_A+OUT_B) schaltet die Motoren in den Rückwärtsgang, und mit Off(OUT_A+OUT_B) werden sie gestoppt. Um den Spybot einem ersten Programmiertest zu unterziehen, wollte ich ihn drei Sekunden vorwärts, dann drei Sekunden rückwärts fahren lassen und schließlich anhalten. Den Quelltext des Programms (Listing 1, vor_u_zurueck.nqc) finden Sie auf der Heft-CD im Verzeichnis LinuxUser/lego/. Der Aufruf

user@host:~$ nqc -TSpy -d vor_u_zurueck.nqc

wandelt das Programm um und lädt es direkt in den Roboter hinein. NQCs Fehlermeldungen, sollte beim Kompilieren etwas schiefgehen, sind so informativ, dass sich Probleme im Quellcode schnell finden lassen. Die Option -TSpy muss für jedes Spybotics-Programm erfolgen; -d sorgt dafür, dass das umgewandelte Programm direkt in den Roboter hineingeladen wird. Dazu muss natürlich die Kabelverbindung stehen und der Spybot eingeschaltet sein. Standardmäßig wird der erste serielle Port angesprochen; mit dem Parameter -S lässt sich aber auch jeder andere Port setzen, z. B. -S/dev/ttyS1.

Kasten 1: NQC-Kontrollstrukturen

if(bedingung)
{konsequenzanweisungen}
else
{alternativanweisungen}
while(bedingung){anweisungen}
do{anweisungen}
while(bedingung)
for(initialanweisung;bedingung;folgeanweisung)
{wiederholungsanweisungen}
repeat(anzahlausdruck)
{anweisungen}
switch(ausdruck)
{
  case konstanter_ausdruck_1:
  {anweisungen_1} break;
  …
  case konstanter_ausdruck_n:
  {anweisungen_n} break;
  default:
  {anweisungen} break;
}
until(bedingung)
{anweisungen}

Agenten-

: vor, zurück und stop mit <I>vor_u_zurueck.nqc<I>

#include "spy.nqh"     // muss jedes Spybot-Programm haben
task main()
{
  OnFwd(OUT_A+OUT_B);  // beide Motoren vorwärts
  Wait(300);           // 3 Sekunden fahren
  OnRev(OUT_A+OUT_B);  // beide Motoren rückwärts
  Wait(300);           // 3 Sekunden fahren
  Off(OUT_A+OUT_B);    // anhalten
}

Im Vorwärtsmarsch zurück

Nachdem ich mein Programm vor_u_zurueck.nqc in den Spybot geladen und gestartet hatte fuhr dieser zunächst einmal rückwärts (statt vorwärts) und änderte dann nach drei Sekunden seine Richtung. Dieses ist kein Fehler im Programm, sondern einfach konstruktionsbedingt. Das Zahnrad setzt direkt am Motor einmal um auf die großen Zahnräder meines Gigamesh G60 (Agentenfoto 6), weswegen in allen Programmen vorwärts und rückwärts vertauscht programmiert werden müssten, wenn NQC dafür nicht den Befehl SetGlobalDirection() hätte. Mit dem Parameter OUT_REV werden alle Kommandos umgekehrt. Weitere Bewegungsbefehle für den Spybot finden Sie in Kasten 2.

Abbildung 5: Umsetzung in Richtung rückwärts

Kasten 2: NQC-Bewegungsbefehle

OUT_A     // rechter Motor
OUT_B     // linker Motor
OUT_V     // Laser
outputs   // ein +-Kombination aus
          // OUT_A, OUT_B und OUT_C (soweit sinnvoll)
On(outputs)          // An
Off(outputs)         // Aus
Fload(outputs)       // Auslaufen lassen
Fwd(outputs)         // Vorwärts
Rev(outputs)         // Rückwärts
Toggle(outputs)      // Richtungswechsel
OnFwd(outputs)       // = On()+Fwd()
OnRev(outputs)       // = On()+Rev()
OnFor(outputs, time) // 100time = 1Sekunde
SetOutput(outputs, mode)
  // mode aus OUT_OFF, OUT_ON, OUT_FLOAT
SetDirection(outputs, direction)
  // direction aus OUT_FWD, OUT_REV, OUT_TOGGLE
SetPower(outputs, power)
  // power aus 0 bis 7 setzt die Geschwindigkeit
x=OutputStatus(n)
  // gibt den Status zurück
SetGlobalOutput(outputs, mode)
  // mode aus OUT_ON, OUT_OFF   Schaltet die generelle
  // Kontrollmöglichkeit von Befehlen der Ausgänge ein oder aus.
SetGlobalDirection(outputs, direction)
  // direction aus OUT_FWD, OUT_REV, OUT_TOGGLE
  // Kehrt alle Befehle um oder setzt wieder auf norm oder wechselt
  // Wichtiger Befehl für den Gigamesh G60
SetMaxPower(outputs, max_power)
  // max_power aus OUT_LOW, OUT_HALF, OUT_FULL
  // Setzt die generelle Höchstgeschwindigkeit der Motoren
x=GlobalOutputStatus(n)
  // gibt generellen Status zurück

Hierarchie

Neben den Tasks gibt es noch Unterprogramme (sub()) und Inline-Funktionen (void()). Der Vorteil von Inline-Funktionen ist, dass man ihnen Parameter übergeben kann – sowohl über call by value als auch über call by reference. NQC-Programme kennen nur integer-Variablen. Agenten-Listing 2 zeigt eine typische Inline-Funktion: Sie wird mit ihrem Namen aufgerufen (zwei_drei();) und darf mit dem Befehl return; vorzeitig beendet und wieder verlassen werden. Als Alternative bietet NQC Unterprogramme, von denen insgesamt 32 pro Programm definiert werden dürfen. Auch wenn an diese keine Parameter übergeben werden können, haben sie den Vorteil, dass sie nur einmal im Speicher vorhanden sind und von mehreren Aufrufern geteilt werden dürfen. Unterprogramme können keine weiteren Unterprogramme aufrufen.

Es dürfen 32 globale und vier lokale Variablen deklariert werden, weshalb diese so lokal wie möglich deklariert werden sollten. Variablen können mit den gleichen Operatoren verknüpft werden, wie man dies von integer-Variablen aus C-Programmen kennt: +, -, *, /, % (mod), abs(), sign(), ++, --, ~ (bitweise Negation), ! (Negation), >>, <<, ==, !=, & (bitweises und), ^ (bitweises XOR), (bitweises oder), && (AND), (OR), >, <, >= und <=.

Die Anzahl der Tasks ist bei einem Spybotics-Programm auf acht begrenzt. Die Tasks laufen parallel, d. h. im Gegensatz zum Aufruf eines Unterprogramms wartet der Aufrufer nicht bis auf dessen Ende, sondern läuft weiter. Auf diese Weise lassen sich mit NQC echte Multitasking-Programme schreiben. Kasten 3 zeigt die einzelnen Task-Befehle.

Agenten-

: Eine NQC-Inline-Funktion

void zwei_drei(int value_var, int &reference_var)
{
  value_var=2;      // value_var behält ihren Wert nur bis zum Ende
  reference_var=3;  // der Wert 3 ist auch nach Aufruf noch vorhanden
}

Kasten 3: Task-Befehle

start taskname;     // startet einen Task
stop taskname;      // stop einen Task
StopAllTasks();     // stoppt alle Tasks

Für meinen Auftrag mit dem Spybot ist natürlich besonders interessant, dass er mit Sensoren ausgestattet ist. Mein Agentenprogramm soll je nach Status des Sensors bestimmte Aktionen koordinieren. Da ist zunächst einmal der Drucksensor, der in NQC über SENSOR_1 angesprochen wird. er Drucksensor wird normalerweise so eingestellt, dass er boolsche Werte, also 0 (Zustand "nicht gedrückt" und 1 (Zustand "gedrückt"), zurückgibt. NQC bezeichnet dies als SENSOR_MODE_BOOL. Ein Lichtsensor () SENSOR_2 gibt prozentuale Werte zurück (0 bis 100) und wird in NQC als SENSOR_MODE_PERCENT beschrieben. Außerdem kennt NQC noch SENSOR_MODE_RAW für den Spybot – es handelt sich um die interne Darstellung im Robotor für den Sensor (zurückgegeben wird ein Wert zwischen 0 und 1024). Die Statusabfrage eines Sensors erfolgt über eine Variablenzuweisung, z. B. x=SENSOR_1, aber auch eine Abfrage, wie z. B. if(SENSOR_1==1), gibt Auskunft darüber, ob der Sensor gedrückt ist. Kasten 4 enthält eine Liste aller Sensorbefehle.

Kasten 4: Sensorbefehle

sensor  // SENSOR_1 oder SENSOR_2n       // 0,1 für SENSOR_1 oder SENSOR_2mode    // SENSOR_MODE_BOOL, SENSOR_MODE_PERCENT, SENSOR_MODE_RAW
SetSensorMode(sensor,mode) // Setzt den Modus für Sensoren
ClearSensor(sensor)        // Nur sinnvoll bei Sensoren, die Impulse oder
  // Umdrehungen zählen und wieder zurückgesetzt werden sollen
SensorValue(n)             // x=SensorValue(0) entspricht x=SENSOR_1
SensorMode(n)              // gibt den Modus zurück
SensorValueRaw(n)          // gibt den internen Sensorwert zurück

Hast Du Töne?

In den Spybot ist auch eine Tonausgabe integriert. Eine Übersicht, mit welchen Befehlen sie angesprochen werden kann, gibt Kasten 5. Den Laser spricht man entweder mit On(OUT_C); oder SendVLL(n); (wobei n zwischen 0 und 127 liegt) an. Bleiben die Befehle, die sich mit internen Timern etc. beschäftigen, und die Event-Steuerung mit bis zu 16 verschiedenen, frei konfigurierbaren Ereignissen. Für meine Zwecke benötigte ich lediglich den Befehl Random(n);, der einen zufälligen Wert zwischen 0 und n zurückliefert. Die weiteren Ergebnisse meiner Recherchen habe ich aber auf die Heft-CD (Agenten-CD?) eingeschleust; alternativ finden sich die Infos unter [3].

Kasten 5: NQC-Töne

PlaySound(sound)          // sound kann sein
  // SOUND_CLICK, SOUND_DOUBLE_BEEP, SOUND_DOWN
  // SOUND_UP, SOUND_LOW_BEEP, SOUND_FAST_UP
PlayTone(frequenz, dauer) // PlayTone(440,50) spielte
  // eine halbe Sekunde den Kammerton a
MuteSound()
  // Stoppt alle Töne und schaltet stumm
UnmuteSound()
  // Reinitialisiert die Tonausgabe und lässt Töne wieder zu
ClearSound()
  // Löscht hinterherhängende Töne aus dem Speicher

Es wird ernst

Mit allem Notwendigen für die Programmierung des Spybots ausgestattet, legte ich mich vor dem besagten Gebäude auf die Lauer. Eine Möglichkeit, nachts unentdeckt ins Gebäude zu kommen, wollte mir einfach nicht einfallen, bis ich schließlich doch die rettende Idee hatte – die Lüftungsschächte. Deren Außenmauern waren nur mit leichten Gittern versehen und würden als Eingangstür für den kleinen Roboter dienen. Als falscher Pizzaverkäufer getarnt nahm ich den Aufbau des Gebäudes und insbesondere dort die Lüftung genauer unter die Lupe. Die Lüftungsschächte verliefen quer durch die Etagen. Große vergitterte Flächen sorgten für die nötige Luftzirkulation.

Damit stand mein Plan fest: Ich würde den Spybot nachts durch eine der leicht gesicherten Öffnungen ins Gebäude bringen, absetzen und dort losfahren lassen. Immer wenn der Spybot über ein Lochgitter führe, stiege die Helligkeit an. Wenn der Roboter das Loch passierte, würde es wieder dunkler – der Lichtsensor sollte dies erfassen können. Danach sollte der Roboter wieder ein Stück zurückzufahren bis aufs Gitter, einen Höllenlärm veranstalten und so die Alarmanlage auslösen, um anschließend schnell abzuhauen.

Im Übergang vom Gitterbereich in den verdeckten ist ein Helligkeitsrückgang von zehn Prozent, was durch periodisches Abfragen des Lichtsensors erkannt wird. Dieses erledigt der Task lichtkontrolle() im NQC-Programm helsinki.nqc (Listing 3, nur auf der Heft-CD). Weiterhin sollte der Spybot zufällig durch die Gänge brausen. An jeder T-Kreuzung oder in Sackgassen sollte er sich (Random()-gesteuert) für eine neue Richtung entscheiden – also eine Links- oder Rechtsdrehung vollführen. Dafür ist der Task hindernis() zuständig. Wichtig war an dieser Stelle, dass sich die beiden Aktionen lichtkontrolle() und hindernis() nicht gegenseitig behindern sollten. Es wäre fatal für den Auftrag, wenn der Spybot in einer Sackgasse ein ständiges Spektakel ausführte, nur weil vielleicht ein unvorhergesehener Lichteinfall aufträte. Um das zu verhindern, verwendete ich für die beiden Tasks die Semaphor-Technik: Ein Task sperrt vor einer Aktion alle anderen durch Setzen einer globalen Variablen sem. Nur wenn diese den Wert 0 hat, beginnt ein Task mit seiner speziellen Aktion. Solange diese ausgeführt wird, wird jede andere durch temporäres Setzen der Variable auf 1 gesperrt.

Abbildung 6: Gitterbereich

Das Programm helsinki.nqc enthält weiterhin eine Unterfunktion warten(), die den Spybot für die erste halbe Stunde an seinem Ort verharren lässt – genug Zeit, mich zu verdrücken. Nach etlichen Testfahrten in meinem Labor konnte ich die Wait()-Zeiten soweit anpassen, dass der Spybot zufriedenstellende 90-Grad-Drehungen fuhr.

Ich schickte Nachricht an den Pinguin, dass ich bereit war. In der verabredeten Nacht schraubte ich einen Spalt ins Außengitter der Belüftung, übergab den Spybot seinem Schicksal und sah zu, dass ich wegkam, denn schon eine gute halbe Stunde später schrillten im Gebäude die ersten Sirenen.

Agenten-

: <I>helsinki.nqc<I>

#include "spy.nqh"
// Semaphor zum gegenseitigen
// Sperren von Anstoßen und Laser
int sem=0;
void spektakel()
{
  until(sem==0)        // warten bis Semaphor frei
  {
    sem=1;             // Semaphor sperren
  }
  PlaySound(SOUND_DOWN);
  Toggle(OUT_A+OUT_B); // zurückfahren
  Wait(100);
  Float(OUT_A+OUT_B);  // auslaufen lassen
  // Drehen
  Rev(OUT_A); Fwd(OUT_B); On(OUT_A+OUT_B);
  repeat(8)
  {
    /*
    // Laser an, Krach schlagen
    */
    SendVLL(0);
    PlaySound(SOUND_FAST_UP);
    Wait(50);
  }
  Off(OUT_C);          // Laser aus
  // auf Schleichfahrt
  MuteSound(); ClearSound(); UnmuteSound();
  OnFwd(OUT_A+OUT_B);  //  abhauen
  sem=0;               // Semaphor freigeben
}
task lichtkontrolle()
{
  int alte_helligkeit;
  int aktuelle_helligkeit;
  int intervall_lichtmessung=50;
  alte_helligkeit=SENSOR_2;       // initialisieren
  while(true) // ständig die Helligkeit beobachten
  {
    aktuelle_helligkeit=SENSOR_2;
    /*
    // mehr als 10% dunkler geworden?
    */
    if(aktuelle_helligkeit+10<alte_helligkeit)
    {
      spektakel();                // Inline-Funktion aufrufen
      // Helligkeit sichern
      alte_helligkeit=aktuelle_helligkeit;
    }
    Wait(intervall_lichtmessung); // Warten auf nächsten Messung
  }
}
task hindernis()
{
  while(true)              // Warten auf Drucksensor
  {
    if(SENSOR_1==1)        // Drucksensor betätigt?
    {
      until(sem==0)        // warten bis Semaphor frei ist
      {
        sem=1;             // Semaphor sperren
      }
      Toggle(OUT_A+OUT_B); // Motoren in umgekehrte Richtung
      /*
      // eine Sekunde in andere Richtung fahren lassen
      */
      Wait(100);
      if(Random(1)==0)     // zufällige Drehrichtigung wählen
      {
        Toggle(OUT_A);
      }
      else
      {
        Toggle(OUT_B);
      }
      Wait(60);            // drehen lassen
      Fwd(OUT_A+OUT_B);    // Beide Motoren wieder vorwärts
      sem=0;               // Semaphor freigeben
    }
  }
}
sub warten()
{
  int zeit=0;
  PlaySound(SOUND_DOUBLE_BEEP);
  ClearTimer(0);
  /*
  // ca. eine halbe Stunde warten
  */
  until(zeit>=15000)
  {
    zeit=Timer(0);
  }
  PlaySound(SOUND_LOW_BEEP);
}
task main()
{
  int geschwindigkeit=7;   // maximale Geschwindigkeit
  // Initialisierung:
  // Drucksensor erwartet "an" oder "aus"
  // Lichtsensor auf 0 bis 100
  SetSensorMode(SENSOR_1,SENSOR_MODE_BOOL);
  SetSensorMode(SENSOR_2,SENSOR_MODE_PERCENT);
  SetPower(OUT_A+OUT_B,geschwindigkeit);
  /*
  // Besonderheit Gigamesh G60 berücksichtigen
  // wo alles umgekehrt definiert sein müsste
  */
  SetGlobalDirection(OUT_A+OUT_B,OUT_REV);
  warten();
  OnFwd(OUT_A+OUT_B);      // Los gehts
  start lichtkontrolle;    // Lichtkontrolle ein
  start hindernis;         // Hinderniskontrolle ein
}

Glossar

wine

ist eigentlich kein echter Emulator, der Windows in Linux emuliert. Vielmehr nimmt wine die System- und Bibliotheksaufrufe an Windows auf und biegt diese in Linux-Aufrufe um. Der LinuxUser hat in seiner Ausgabe 10/2000 ausführlich über wine berichtet [1].

User.ini

Datei mit benutzerspezifischen Konfigurationen von Programmen, die auf unter Windows (oder eben unter wine) laufen.

call by value

Nur der Wert eines variablen Parameters wird übergeben. Ändert sich der Wert des Parameters innerhalb der Funktion, hat er nach Funktionsende den ursprünglichen Wert.

call by reference

Die Änderungen eines variablen Parameters innerhalb einer Funktion bleiben auch nach Ende der Funktion noch erhalten. Eine solche Übergabe wird in NQC mit dem Symbol & im Funktionskopf deklariert.

Infos

[1] Peter Ganten: "Windows-Programme unter Linux – Red, Red Wine", LinuxUser 10/2000, S. 15 http://www.linux-user.de/ausgabe/2000/10/015-wine/wine.html

[2] Die wine-Homepage: http://www.winehq.com

[3] NQC: http://www.baumfamily.org/nqc/

Der Autor

Volker Schmitt ist Mathematiker und arbeitet bei einer großen Versicherung. Multitasking-Programmierung kennt er aus seiner PL/1-Zeit am Großrechner; dank NQC kann er sich mit seinem Spybot von LEGO so richtig austoben.

LinuxCommunity kaufen

Einzelne Ausgabe
 
Abonnements
 

Related content

Kommentare