Ostereiersuche mit NanoAxe-Controller

Seite 4: Quellcode

Inhaltsverzeichnis

Im Folgenden beschreiben wir ausführlich den BASIC-Quellcode für den Nano-Axe-Controller. Sie können das Projekt natürlich auch ohne tieferes Verständnis aufbauen, dann geht es für Sie auf der nächsten Seite weiter. Ganz oben im Quellcode kann mit CODE_DIGIT1 bis CODE_DIGIT4 der gewünschte vierstellige Pincode definiert werden. Wenn für Testzwecke eine Debugausgabe auf dem Picaxe-Terminal gewünscht ist, muss das Kommentarzeichen (Semikolon) beim Define ENABLE_DEBUG_OUTPUT entfernt werden. Dadurch werden alle mit DEBUGTEXT aufgerufenenTextausgaben mit dem sertxd-Befehl ausgegeben. Hiermit können dann beispielsweise die erkannten Codeziffern auf dem Terminal sichtbar gemacht werden. Anschließend werden die Portpins für die LEDs, den Hallsensor sowie den Servomotor definiert. Außerdem werden die beim servopos-Befehl benötigten Werte für die beiden Servostellungen „verriegelt“ und „entriegelt“ festgelegt.

#picaxe 08m2

; 4-stelligen Pincode fuer das Codeschloss definieren
#define CODE_DIGIT1 1
#define CODE_DIGIT2 2
#define CODE_DIGIT3 3
#define CODE_DIGIT4 4

; wenn keine Debugausgabe per Terminal gewuenscht, dann folgende Zeile auskommentieren
;#define ENABLE_DEBUG_OUTPUT

symbol LEDS_PIN = C.1
#define LEDS_OFF   low LEDS_PIN
#define LEDS_ON    high LEDS_PIN

symbol HALL_PIN = pinC.3
symbol HALL_PULLUP_MASK = %01000

symbol SERVO_PIN = C.4
symbol SERVO_POS_CLOSED = 150 ; Servo Position 90 Grad = verriegelt
symbol SERVO_POS_OPEN = 240   ; Servo Position 180 Grad = entriegelt

#ifdef ENABLE_DEBUG_OUTPUT
   #define DEBUGTEXT sertxd
#else
   #define DEBUGTEXT ;
#endif

symbol RAM_ADDR_KEYCODE = 28
symbol counter = b0
symbol codeInputActive = b1
symbol codeDigit = b2
symbol timeout = b3


pullup HALL_PULLUP_MASK
gosub initPincode
codeInputActive = 0
codeDigit = 0
timeout = 0
LEDS_ON
do
loop until HALL_PIN = 0
gosub lock
do
loop while HALL_PIN = 0
LEDS_OFF
pause 500
main:
   if HALL_PIN = 0 then
      codeInputActive = 1
      LEDS_ON
      pause 250
      LEDS_OFF
      pause 1000
      codeDigit = codeDigit + 1 % 10
      DEBUGTEXT(" ", #codeDigit)
      timeout = 0
   else
      if codeInputActive = 1 then
         gosub checkCode
         codeInputActive = 0
         timeout = 50 ; Timeout 50*100ms = 5s
         codeDigit = 0
         LEDS_ON
         pause 1000
         LEDS_OFF
         pause 500
      endif
   endif
   pause 100
   if timeout != 0 then
      dec timeout
      if timeout = 0 then
         DEBUGTEXT(cr,lf, "Timeout",cr,lf)
         for counter = 1 to 3
            LEDS_ON
            pause 250
            LEDS_OFF
            pause 250
         next counter
         gosub initPincode
      endif
   endif
goto main
   
            
checkCode: ; Code ueberpruefen
   if codeDigit = @bptr then
      DEBUGTEXT(cr,lf, "Codeziffer richtig",cr,lf)
      inc bptr
      if @bptr = 0xFF then
         LEDS_ON
         DEBUGTEXT(cr,lf, "*** Code richtig ***",cr,lf)
         gosub unlock
         do
            LEDS_OFF
            pause 500
            LEDS_ON
            pause 500
         loop
      endif
   else
      DEBUGTEXT(cr,lf, "Codeziffer falsch",cr,lf)
      gosub initPincode
   endif
return


lock: ; Schloss verriegeln
   DEBUGTEXT(cr,lf,"Lock",cr,lf)
   pause 100
   servo SERVO_PIN, SERVO_POS_CLOSED
   servopos SERVO_PIN, SERVO_POS_CLOSED
   pause 1000
return


unlock: ; Schloss entriegeln
   DEBUGTEXT(cr,lf,"Unlock",cr,lf)
   pause 100
   servopos SERVO_PIN, SERVO_POS_OPEN
   pause 1000
   servo SERVO_PIN,OFF
return


initPincode: ; Initialisierung des Pincodes
   bptr = RAM_ADDR_KEYCODE
   @bptrinc = CODE_DIGIT1
   @bptrinc = CODE_DIGIT2
   @bptrinc = CODE_DIGIT3
   @bptrinc = CODE_DIGIT4
   @bptrinc = 0xFF
   bptr = RAM_ADDR_KEYCODE
return

Beim Programmstart wird zunächst der interne Pullup für das Hallsensormodul aktiviert. Im Unterprogramm initPincode wird der oben festgelegte Pincode ins RAM ab der Adresse RAM_ADDR_KEYCODE geschrieben, gefolgt vom Wert 0xFF als Endekennung. Die Variable bptr wird auf den Wert RAM_ADDR_KEYCODE gesetzt, sodass diese als Pointer auf die erste Codeziffer zeigt. Anschließend werden die beiden LEDs eingeschaltet, sodass die Augen des Hasen leuchten. Es wird nun so lange gewartet, bis der Hallsensor ein Magnetfeld erkennt, d.h. ein LOW-Pegel auftritt. Hierzu muss die Karotte an die richtige Position unterhalb der Nase des Hasen gehalten werden. Dann wird die Truhe durch Aufruf des Unterprogramms lock verriegelt, wodurch der Servomotor in die Verriegelungsposition (Mittelstellung) bewegt wird. Anschließend wird so lange gewartet, bis der Hallsensor kein Magnetfeld mehr detektiert und dann die LEDs ausgeschaltet. Ab jetzt ist das Codeschloss scharf geschaltet.

Die main-Schleife prüft, ob der Hallsensor ein Magnetfeld erkennt. Wenn ja, beginnt die Codeeingabe und die Variable codeInputActive wird auf 1 gesetzt. Die beiden LEDs leuchten kurz auf (250ms an, 1s aus) und die Variable codeDigit wird um 1 erhöht, da diese die Pulse für die Eingabe der aktuellen Codeziffer mitzählt. Außerdem wird die Variable timeout auf 0 zurückgesetzt. Nach dem endif-Befehl (Zeile 70) wartet das Programm 100ms, die anschließende if-Bedingung für die timeout-Variable ist nicht erfüllt, sodass sie wieder an den Beginn der main-Schleife springt. Erkennt der Sensor immer noch ein Magnetfeld, dann wird der nächste Lichtpuls ausgegeben und codeDigit erneut erhöht. Wie weit der Eingabewert für die aktuelle Codeziffer hochgezählt wird, ist abhängig davon, wie lange wir die magnetische Karotte unter die Nase des Hasen halten.

Dies wiederholt sich also so lange, bis kein Magnetfeld mehr erkannt wurde, sodass dann der else-Zweig ab Zeile 60 ausgeführt wird. Wenn die Variable codeInputActive eine laufende Codeeingabe anzeigt, dann prüft das Unterprogramm checkCode, ob die gerade eingegebene Ziffer korrekt ist. Hierzu vergleicht sie den Wert von codeDigit mit dem RAM-Wert, auf den bptr zeigt. Wenn diese unterschiedlich sind, wird im else-Zweig ab Zeile 104 durch Aufruf von initPincode die Variable bptr wieder auf den Anfang im RAM zurückgesetzt, sodass der bisher eingegebene Pincode komplett verworfen und mit return zurückgekehrt wird.

Bei richtiger Codeziffer wird hingegen der if-Zweig ab Zeile 90 ausgeführt und bptr inkrementiert, sodass dieser Pointer auf die nächste erwartete Codeziffer zeigt. Falls diese den Wert 0xFF hat, dann wurden alle 4 Codeziffern richtig eingegeben und das Unterprogramms unlock bewegt den Servomotor in die Entriegelungsposition, sodass sich die Schatztruhe nun öffnen lässt. Die LED-Augen blinken nun in einer Endlosschleife, denn die Osterrallye wurde erfolgreich abgeschlossen. Falls der Code jedoch noch nicht komplett eingegeben wurde, wird checkCode mit return verlassen.

Nach dem Rücksprung aus checkCode geht es ab Zeile 62 weiter. Die Variable codeInputActive wird zurückgesetzt, da die Eingabe der aktuellen Codeziffer beendet ist. Nun wird ein Timeout von ca. 5 Sekunden (50 * 100ms) über die entsprechende Variable vorbereitet und codeDigit auf Null zurückgesetzt, damit die nächste Codezifferneingabe wieder mit 1 startet. Die LEDs leuchten eine Sekunde lang auf und signalisieren dadurch optisch die abgeschlossene Eingabe der aktuellen Codeziffer. Nach einer Pause von 100ms in Zeile 71 ist nun die if-Bedingung erfüllt. Somit wird die timeout-Variable dekrementiert. Da sie jedoch noch nicht auf Null steht, erfolgt wieder ein Sprung an den Beginn der main-Schleife. Falls kein Magnetfeld erkannt wird, erfolgt lediglich eine Pause von 100ms (Zeile 71). Wenn also 5 Sekunden lang kein Magnetfeld erkannt wurde, ist der Timeout abgelaufen und verzweigt in Zeile 75. Die LEDs blinken dreimal schnell hintereinander und signalisieren so optisch, dass die Codeeingabe aufgrund zu langer Inaktivität zurückgesetzt wurde. Der Pincode muss also wieder komplett neu eingegeben werden.

Wenn jedoch während der 5-sekündigen Timeoutphase ein Magnetfeld erkannt wird, geht es mit Zeile 51 weiter, wo die Eingabe der nächsten Codeziffer verarbeitet wird. Es bleibt also nach jeder eingegebenen Codeziffer ein Zeitfenster von rund 5 Sekunden, um die nächste Ziffer einzugeben.