Lekce 2 - NXJ - základy API a první užitečný program
V minulém dílu, Seznámení s NXJ pro LEGO NXT, jsme si zprovoznili vývojové prostředí pro NXJ.
Dnes si konečně uděláme něco užitečného. Pokud jste si procházeli NXJ firmware na NXT, asi jste si všimli absence nějaké utilitky na odečítaní hodnot z vestavěných rotačních senzorů v motorech a ostatních senzorů. Přesně tento nedostatek dnes částečně zaplníme. Zároveň nám to poskytne prostor k představení některých ze základních tříd, kterými NXJ API disponuje.
Užitečné třídy API
Nejdříve si pojďme představit třídy, se kterými budeme pracovat. Před
tím ale ještě trochu terminologie. Budu používat termín “blokující
metody” (nevím, jestli to můžu prostě přeložit z “blocking method”,
takže mě klidně opravte). Znamená to, že se vlákno zastaví a čeká na
její vykonání. Takovým příkladem může být třeba
Button.waitForAnyPress().
Motor
Bezesporu jedna z nejpoužívanějších tříd a to ať už přímo, nebo nepřímo, poněvadž jen zřídkakdy bude výstupem programu jen obraz na displeji nebo zvuk. Nejdříve bychom si měli sdělit něco o hardwarové povaze motorů. Jak jsem již zmínil, mají vestavěné rotační senzory, které dokáží s přesností ±1° snímat fyzické natočení výstupu (oranžová část). Zároveň mají nastavitelnou rychlost, a sice ve stupních za sekundu. Maximální rychlost == 100 * napětí dodávané bateriemi, proto závisí na tom, zda používáte alkalické baterie (6*1,5V=9V => maximální rychlost= 900°/s), nebo nabíjecí (6*1,2V=7.2V, plně nabité i přes 8V => používejte rychlost cca 700°/s, 7.2V jsou už většinou baterie skoro vybité). Samotná třída Motor nic neumí, ale o to zajímavější jsou statické instance třídy NXTRegulatedMotor: A, B, a C. Jistě víte, že NXT nabízí výstupy pro 3 motory, které se právě těmito písmeny označují.
NXTRegulatedMotor
Tato třída reprezentuje motor připojený na daný port a k neužitečnějším metodám patří:
rotate(int angle)
- otočí motor o daný počet stupňů relativně k současné pozici; blokující metodarotate(int angle, boolean immediateReturn)
- stejná jako předchozí, ale pokud uvedete druhý parametr true, nebude metoda blokujícírotateTo(int angle)
- otočí motor do absolutní pozice k nulovému bodu na nehybném tělu motoru. Blokující, disponuje stejným přetížením jako metodarotate()
forward()
- motor se začne točit dopředu, dokud ho jinou metodou nezastavítebackward()
- stejně jako předchozí, ale točí se pozpátkustop()
- jedna z metod, která zastavíforward()
nebobackward()
. Po zavolání si motory “zabrzdí” a nelze s nimi otáčet. Blokující, ale zastavení je prakticky okamžité (pokud nezastavujete setrvačník)
flt()
- další metoda k zastavení, ale tentokrát se bude motor volně protáčet. Opět teoreticky blokující, ale nemá smysl se tím zabývatsetSpeed(int/float speed)
- nastavuje rychlost v °/sgetTachoCount()
- vrací celkovou relativní rotaci interního rotačního senzoru typu int ve stupníchresetTachoCount()
- vynuluje interní rotační senzor. (Přesně tohle bude jádro našeho programu) Pozor: na konci se zavolá stop()
LCD
Jak již název napovídá, tato statická třída reprezentuje LCD na NXT. Displej disponuje rozlišením 100*64px a umí jen 2 barvy- černou a bílou (ona není bílá, ale budeme jí tak říkat), nic mezi tím, pokud jste nad tím přemýšleli. Ještě trochu terminologie - LCD znamená liquid crystal display, proto nepoužívejte spojení “LCD displej”, je to barbarské. Metody:
clear()
- smaže veškerý obsah displejedrawString(String str, int x, int y)
- vykreslí str na určených souřadnicích. Nejedná se o souřadnice v pixelech, ale ve znacích. Na jednu z 8 řádek se vejde 16 znaků. Všechny znaky jsou stejně velké (6*8px). Příklad: LCD.drawString(“hi”,7,3) vykreslí “hi” přibližně uprostřed. Souřadnice pochopitelně začínají nulou.drawInt(int i, int x, int y)
- stejné jako předchozí metoda, jen umí kreslit pouze čísla.setPixel(int x, int y, int color)
- vykreslí jediný pixel na [x,y], kteréžto souřadnice jsou v pixelech. Barva může nabývat pouze hodnot 1, nebo 0 (1=černá, 0=bílá)scroll()
- posune poslední řádek vypsaný pomocí System.out úplně nahoru
Sound
Další statická třída určená jako výstup informací. Ačkoliv se jedná o relativně nepodstatnou část, zjistíte, že ji budete používat relativně často. Metody:
setVolume(int vol)
- nastaví hlasitost reproduktoru na hodnotu 0-100. Pokud chcete pracovat se zvukem, nezapomeňte ji použít, protože výchozí hlasitost je téměř neslyšitelná.- metody pro pípání a podobně, nic, co by stálo za rozebrání
Button
Poslední třídou, kterou si dnes uvedeme, bude třída pro práci s tlačítky. Na NXT najdeme 4 tlačítka - ENTER, ESCAPE, LEFT, RIGHT, každé z nich je reprezentováno číselným id a zároveň instancí třídy Button. Jedinou užitečnou statickou metodou je:
waitForAnyPress()
- s touto metodou jsme se už setkali, zastaví běh programu, dokud není stlačeno některé z tlačítek, které vrátí jako id typu int. Id je konstanta na Button, vypadá asi takto: Button.ID_ENTER.
Instanční metody jsou mnohem zajímavější, resp. užitečnější:
isUp()
- vrací boolean, jestli je tlačítko stlačenéisDown()
- asi nemá cenu popisovat
První užitečná utilitka
Založte si nový projekt (opravdu si ho založte, nepřepisujte “hello world”, později budeme pracovat s jednotlivými projekty, a proto se hodí mít více než jeden) “Motor measure” a přidejte hlavní třídu Main. Všimněte si, že používám anglický název, a protože je to velmi dobrý zvyk, měli byste s anglickým pojmenováváním začít (pokud to už ovšem neděláte). Tento styl budu používat během celého seriálu.
Každý návrh programu musí nutně začít otázkou: Co vlastně chci
udělat? Naše současná odpověď by byl program, který bude odečítat
rotaci motorů a bude nám je v reálném čase zobrazovat na displeji + bude
umožňovat vynulování hodnot. Další otázka bude: Jak? Vzhledem k tomu, že
se jedná o opravdu jednoduchý program, použijeme adekvátně jednoduché
řešení - necháme běžet nekonečnou smyčku, která bude neustále
přepisovat hodnoty na LCD na aktuální. Na začátku cyklu se navíc
ujistíme, že není stlačené žádné tlačítko - pokud zjistíme, že ano,
provedeme - v případě ENTERu vynulování, v případě ESCAPE ukončení
programu. Vystačíme si s metodou main(String[] args)
public static void main(String[] args) throws Exception { NXTRegulatedMotor[] motors = {Motor.A, Motor.B, Motor.C}; // abychom nemuseli opisovat vše pro každý motor zvlášť, budeme je // procházet cyklem char[] names = {'A', 'B', 'C'};// písemné označení motorů na stejných // indexech jako v poli motors while (Button.ESCAPE.isUp()) { LCD.clear();//smazat vše na displeji boolean reset = Button.ENTER.isDown();// chceme vynulovat motory? for (int i = 0; i < motors.length; i++) { NXTRegulatedMotor motor = motors[i];// motor se kterým pracujeme if (reset) { motor.resetTachoCount();// vynulovat motor.flt();// motory se po vynulování zastaví, takto se // budou volně protáčet } LCD.drawString(names[i] + " " + motor.getTachoCount(), 0, i); // v NXJ nefunguje String.format() } Thread.sleep(50);//uspat vlákno na 50ms //kdyby to tu nebylo, displej by podivně problikával } }
Asi nic v kódu není nějaká záhada. Všimněte si, že si uložíme stav
tlačítka ENTER na začátku cyklu místo toho, abychom přímo v kódu
použili Button.ENTER.isDown()
. Děláme to proto, že teoreticky
se může stav tlačítka mezi jednotlivými cykly změnit, a takový problém
určitě řešit nechceme. Další zajímavost je, že nemůžeme použít
String.format()
. Proč? Protože to nikdo neimplementoval. NXJ
nevyužívá přímo knihovny Javy, ale vlastní. Program si nahrajte a
vyzkoušejte. To je pro zatím vše.
Příště, NXJ - Senzory a ladění, se budeme kódu věnovat zase o něco méně.