Lézerharc házilag az Arduinóval
Nincs lehetőséged felkeresni kedvenc lasertagarénádat, vagy csak gyakorolnál a következő összecsapásra? Az Arduino készen áll a kihívásra.
Tudományos-fantasztikus játékok és filmek leggyakrabban használt kellékei közé tartoznak a sugárnyalábokat kilövő fegyverek, de nem kell titkos katonai bunkereket felkeresned ahhoz, hogy élesben is megtapasztald, milyen érzés fénypisztolyokkal leszedni az ellenfelet.
Elég ellátogatnod a legközelebbi lézerharcarénába, és csapatoddal máris egy virtuális háborúban találod magad, ahol a résztvevők testére szerelt érzékelők révén minden egyes jól irányzott sugárnyalábbal találatokat szerezhetsz. A következő két oldalon megmutatjuk, miként ültetheted át kompakt méretbe a lasertag koncepcióját, és segítünk egy erre épülő reflexjáték elkészítésében is.
Ami nélkülözhetetlen
Az alapszolgáltatások összeállításához és a tesztkörnyezet elkészítéséhez nem lesz szükséged igazán sok eszközre, ugyanakkor ha nagyobb szabású projektben gondolkozol, akkor a főbb kiegészítőket beszerezve gyorsan bővítheted házi lőteredet, a könnyű skálázhatóságnak köszönhetően. Az alapprojekt egy egyszerű feladatot hajt végre: a szervomotor emelőjére rögzített fényszenzort egy gombnyomás hatására felemeli, és amikor a megfelelő pozícióba került a "céltábla", akkor várja a lövéseket.
Amikor pedig bekövetkezik a találat, visszaugrik az eredeti pozícióba. Ez természetesen csakis a már említett szervomotor, valamint a fényérzékelő szenzor birtokában kivitelezhető, ezek összehangolásával alakíthatod ki a reflextesztet. Ha te is az általunk összeállított verziót szeretnéd elkészíteni, akkor mindenképp csapj egy nyomógombot és egy LCD-kijelzőt is az eszközkészlethez. Persze ezzel még nincs vége az anyagigénynek, a szóban forgó alkotás megvalósítása során ugyanis kifejezetten fontos szerepet kap a körítés.
Mi legót használtunk a pálya felépítéséhez, de természetesen egyéb alternatív megoldásokat is választhatsz a környezet kialakítására (például kartonból kivágott figurák is tökéletesen megfelelnek a célnak). Mindezek mellett a legfontosabb komponensről, a lézerről se feledkezz meg: ha valamilyen pointer a rendelkezésedre áll, akkor már probléma nélkül elboldogulsz a tesztek alatt. Igény szerint természetesen ehhez a lézerhez is kialakíthatsz valamilyen foglalatot, ez már tényleg csak rajtad múlik.
Kódra fel!
Első lépésként indítsd el az Arduino programozási környezetét, és az új kód első sorába (még a setup() függvény elé) gépeld be a #include <Servo.h> utasítást, így válnak ugyanis elérhetővé a szervomotorhoz tartozó beépített funkciók. Ezek után hozz létre a következő sorban egy Servo myservo; objektumot, majd nevesítsd a gombhoz tartozó pint (const int button = 3;), valamint a fényszenzorral összekötött aljzatot is (const int light=A2;).
Továbbra is a setup előtt add meg azt a kezdő pozíciót, ahonnan a motort szeretnéd elindítani (int pos = 90;), és még két logikai változót is élesíts (bool servoStarted=false; bool measureLight=false;) - ezekre a feltételek kezelése során lesz szükséged. Ha kész vagy, áttérhetsz a setup() belsejébe, ahol állítsd be bemenetként a gombot (pinMode(button, INPUT);), rendeld a motort ahhoz a pinhez, amelyikre kötötted (myservo.attach(A0);), majd a myservo.write(pos); utasítással forgasd el a kiindulási állapotába.
Tesztelési céllal még a soros monitor aktiválását is helyezd el itt (Serial.begin(9600);), ezt a későbbiekben akár majd ki is veheted. Így már a kezdeti beállítási opciókat el is végezted, ugorhatsz a loop()-ba. A folyamatosan ismétlődő kódrészletben az első bemeneti információ, amire figyelned kell, a gombnyomás. Ezt az if(digitalRead(button)){} feltételvizsgálattal ellenőrizheted. Amint ez bekövetkezik, a servoStarted=true; segítségével rajtolhat el a motorhoz tartozó folyamat.
A következő feltételbe akkor lépj be, ha a servoStarted felvette az igaz értéket (if(servoStarted){}), a belsejében pedig kezdd el a pozíció értékének csökkentését vagy növelését, attól függően, hogyan helyezted el a motort (csökkentés esetén pos-=1;), add át ezt az értéket a vezérlőnek (myservo.write(pos);), majd hasonlítsd össze ezt a frissen beállított számot a célpozícióval (csökkenő esetben if(pos==0){}).
Amennyiben ez teljesül, a servoStarted váltson hamisra, és aktiváld a fényérzékelőhöz tartozó feladatrészt (measureLight=true;), végezetül pedig szúrj be némi késleltetést a delay(10); utasítással.
Lézeres finomhangolás
Mihelyst idáig eljutottál, jöhet némi játék a fénnyel. Először is az if(measureLight){}-ra lesz szükséged, mert csak akkor léphetsz be ebbe a részébe a kódnak, ha a szervomotor a megfelelő állásba helyezte a céltáblaként használt fényérzékelőt. Olvasd be egy változóba az aktuálisan mért világosság értékét (int sensorValue=analogRead(light);), amit a soros monitorra írass is ki (Serial.println(sensorValue);).
Ez csak a kezdeti tesztelési fázisban játszik szerepet, mert így a környezeti fény értékeit megfelelően kalibrálva kiderítheted, mekkora küszöbértéket kell választanod ahhoz, hogy a szenzor csupán a lézer közvetlen sugarait érzékelje. Helyezz el egy kis késleltetést (delay(100);), és indítsd is el a szoftvert. Próbáld ki mindazt, amit eddig megcsináltál, majd ha az ellenőrzésben eljutottál a fényérzékelésig, a soros monitort figyelve jegyezd fel a közvetlen sugárzás során tapasztalt mérési információkat.
Nálunk 750 környéki értéket jelzett a szenzor a lézeres megvilágításkor, emiatt ennél valamivel alacsonyabb küszöbhöz, 730-hoz kötöttük a találatjelzéshez tartozó feltételt (if(sensorValue>730){}). Amennyiben ez teljesül, a pozíciót állítsd vissza a kiindulási értékre és a motornak is add át ezt az információt (pos=90; myservo.write(pos);), a measureLight értéke pedig legyen hamis. Most tehát elkészültél az első prototípussal; gombnyomásra aktiválhatod a játékot, a céltábla eltalálását követően pedig ismételten várja az indításhoz szükséges inputot.
Kijelzővel kiegészülve
Tekintve, hogy az alapok már a rendelkezésedre állnak, eljött az idő, hogy egy kis teljesítménymérést is csempéssz az alkotásodba. Ehhez az I2C-porthoz csatlakoztatott megjelenítőt kóddal is életre kell keltened.
Ugorj a szoftver elejére, és biggyeszd ide a #include <Wire.h> és a #include "rgb_lcd.h" utasításokat (ha valamelyikre hibaüzenetet dobna a program, a Vázlat menüpontban a Könyvtár tartalmazása alatt telepítsd a hozzájuk tartozó csomagokat), majd a szervomotorhoz tartozó elem után egy kijelzőkomponenst is készíts el (rgb_lcd lcd;), illetve egy időzítőként használható változóval is bővítsd a megoldásodat (float timer=0;).
A setup belsejében az lcd.begin(16, 2); sorral aktiváld a képernyőt, az lcd.setRGB(255, 255, 255); segítségével állíts be fehér háttérvilágítást, majd a felső sorba írd ki a játékod nevét az lcd.print("tetszőleges szöveg"); használatával. A loopba átlépve navigálj a servoStarted ágnak arra a sorára, ahol a pozíciót vizsgálod, és itt állítsd át nullára az időzítőt (timer=0;), amit a measureLight vizsgálatakor írass ki a képernyőd alsó sorába (lcd.setCursor(0, 1); timer+=0.1; lcd.print(timer);).
Ezzel elkészült az időzítő, és a kijelzőt is könnyedén használhatod, amelyen a találat leadását követően az aktuálisan elért szintidő látható marad.
Fejlesztési lehetőségek
Nagyon egyszerűen bővítheted a célpontokat, és kiterjesztheted a játékmenetet, ha újabb szervomotorokat és fényszenzorokat adsz a már meglévők mellé. Ilyenkor érdemes lehet a gombot valamilyen automatikusan és véletlenszerűen aktiválódó metódusra cserélni, mert ezáltal jelentősen növelheted az élvezeti faktort. Sőt ekkor már inkább a pontgyűjtést javasolt előtérbe helyezni a gyorsaság helyett, az időzítőt legfeljebb a motorok alapértelmezett pozícióba kapcsolására használd kellően lassú reakció hatására. Megfontolhatod még további mikrovezérlők csatlakoztatását is, ám hogy ez sikerüljön, el kell merülnöd az SPI-kommunikáció rejtelmeiben is.
Kód
#include <Servo.h>
#include <Wire.h>
#include "rgb_lcd.h"
Servo myservo; rgb_lcd lcd; float timer=0; const int button = 3; int pos = 90; bool servoStarted=false; bool measureLight=false; const int light=A2;
void setup() { lcd.begin(16, 2); lcd.setRGB(255, 255, 255); lcd.print("LASER GAME"); pinMode(button, INPUT); myservo.attach(A0); myservo.write(pos);Serial.begin(9600); }
void loop() {
if(digitalRead(button)){ servoStarted=true;}
if(servoStarted){pos-=1; myservo.write(pos); delay(10); if(pos==0){servoStarted=false; measureLight=true; timer=0; } }
if(measureLight){int sensorValue=analogRead(light); Serial.println(sensorValue); if(sensorValue>720){ pos=90; myservo.write(pos); measureLight=false; } lcd.setCursor(0, 1); timer+=0.1;lcd.print(timer);delay(100); } }