13. IDE-s programmeerimisest kokkuvõtvalt
<- eelmine peatükk
järgmine peatükk ->
13.0. Seosed eelmiste peatükkidega
Kõigis peatükkides, välja arvatud esimeses, oleme midagi programmeerinud või vähemalt pidanud õpikust oma IDE-sse programmi kopeerima. Seejärel oleme pidanud programmist aru saama ning vajaduse korral viima selles sisalduvat teksti vastavusse vooluahelaga. Kohati on õpilast ka suunatud iseseisvalt internetist õiget vastust otsima.
Sedakorda vaatame üle seni vaja läinud programmeerimis-tarkused. Kokkuvõte aitab kinnistada õpitut.
13.1. Eesmärk ja selgitused
Peatüki eesmärgiks on anda ülevaade peatükkides sisalduvast Arduino IDE-s kasutatavast programmeerimiskeelest. Tegelikult on Arduino programmeerimiseks kasutatav materjal oluliselt suurem siinkohal esitatust:
vaata näiteks https://www.arduino.cc/reference/en/#functions.
Kui see peatükk läbi uurida ja uuritu ka meelde jääb, siis saavutab õpilane täiendava enesekindluse õppematerjalis sisalduvate programmide lugemisel-mõistmisel ning neisse muudatuste tegemisel. Oleme eelnenud peatükkides edasi liikunud samm-haaval. Selles peatükis kordame õpitut.
Kui see peatükk juhtub rohkem aega võtma, siis on mõistlik jagada materjali jõukohasteks etappideks. Selle tunni materjali saab kasutada ka lihtsalt vajalike töövõtete meenutamiseks. Kui harjutusi teha ei jõua, siis soovitan järgnevat lihtsalt lugeda—midagi jääb meelde igal juhul. Hiljem võimaliku vajaduse korral saate seda peatükki kasutada kiireks mäluvärskenduseks.
13.2. Töövahendid:
13.3. Tegevused:
13.3.1. Arduino IDE kohustuslikust programmi-struktuurist
Igal Arduino programmil peab olema minimaalselt kaks osa—et üldse kompileeruks.
Neile kahele lisasime ette veel nn muutujate osa. Meenutame, milleks oli vaja muutujaid eraldi alguses välja tuua?
Harjutus A
Pange käima uus Arduino programm (IDE pealt File-> New (või Ctrl + N). Seejärel klikkige see kompileerima ning seejärel tehke programmi laadimine Arduinole. Kas toimus nii kompileerimine kui ka allalaadimine? Kas toimub ka siis, kui sisse jätta vaid void setup()? Vaid void loop()?
- void setup() osas tehakse asju, mida tehakse üks kord—see on üldjuhul ettevalmistav osa.
- void loop() osas tehakse asju, mida tehakse mitu korda ja mis jäävadki korduma.
Harjutus B
Pange kirja, kuidas oleme kasutanud void setup() osa.
Nt. oleme selles osas andnud korralduse Serial.begin();
Ent mida veel?
Tehke üldistusi ja tooge näiteid. Selle töö tarvis käige läbi õpikus eespool kasutatud näidisprogrammid.
Harjutus C
Pange kirja, kuidas oleme kasutanud void loop() osa.
13.3.2. Muutujad, võtmesõnad
Muutujaid oleme deklareerinud eelkõige enne void setup() osa. Ent lokaal-muutujateks nimetatavaid muutujaid oleme deklareerinud ka mõnes muus programmiosas. Millises nimelt?
13.3.2.1. Keelatud sõnakasutus
Muutujate juures peame rääkima ka Arduino programmis reserveeritud sõnadest (inglise keeles keywords): neid ei saa kasutada muutujate nimedena. Toon näiteks sõnakuju HIGH. Võime ka mõelda nii, et mis muudab programmis värvi (kirjutage IDE-sse sõna delay ja vaadake, kas muudab värvi), see ei sobi muutuja nimeks. Aga näiteks PunaneLED sobib.
Harjutus D
Katsetage ja seejärel kommenteerige järgmisi:
Vaadake korra kolmanda tunni materjalidest, mida rääkisime muutujatest pikemate programmitekstide korral—kas muutujate abil on lihtsam muuta üksikuid väärtusi?
13.3.2.2. Muutuja tüüp: vaatleme tüüpe char, String ning int, float
- char ja String:
char võtab ühe baidi väärtuses mälumahtu ühe sümboli tarvis. Neid kirjutatakse: 'a' , 'b' , 'c'. Kui tahame kirjutada kokku mitut sümbolit, siis on tüübiks String—näiteks „abc“. (Tüübi String kohta on eesti keeles kasutusel sõne.). Varasemalt oleme vaadelnud, et sümboleid hoitakse mälus numbrilisel kujul, oleme uurinud näiteks ASCII tabelit. Deklareerida saab char tüüpi muutujat järgmiselt
char A_symbol = 'A';
NB!
Kui panna muutuja nimesse täpitähti—näiteks kirjutada char sümbol = ’A’, saame kompileerimisel kindlasti veateate.
Siinkohal piisab, kui märgime, et sõne (String) käib jutumärkide sisse. Oleme kasutanud jutumärke “ ” programmi tekstis—seega oleme kasutanud String tüüpi—ilma eraldi muutujana deklareerimata kujul.
Toome näite String tüüpi muutuja deklareerimisest:
String pikkJutt = “üks pikk jutt”;
NB!
Tüüp String on suure algustähega!
- int ja float:
Oleme enamusel juhtudel kasutanud int tüüpi. See on täisarvu tüüp. Kasutame seda tüüpi deklareerimaks konkreetset viiku—näiteks mingi sensori signaali lugemiseks.
Süntaksinäide:
int sensoriViik = A0; int ooteaeg = 1000; /*näiteks millisekundites delay() funktsiooni väärtust muutujana ette andes*/
Mida me aga kasutanud ei ole, on float-tüüp.
Vajalik komadega (st inglise traditsioonis punktidega) arvude väljendamiseks. Eestikeelne nimetus float andmetüübi jaoks on ujukomaarv (floating point number).
1 vt https://www.arduino.cc/en/Reference/ASCIIchart
Harjutus E
Pange käima järgmine programmitekst.
int IntegerMuutuja = 1; // ei saa deklareerimisel kasutada täpitähti, seepärast ei kirjuta muutuja nimeks Täisarv... float UjukomaarvMuutuja = 1.123456789; float KomaMuutuja_9_kohaline = 1.123456789; char symbolTavaline = 'S'; char symbolTavalineArray [7] = "abcDEF1"; char eestiKeelesArduino[] = "Arduino"; String Question = "kas on küsimusi täpitähtede kohta? või on küsimusi float-tüübi kohta?"; void setup() { Serial.begin(9600); /* ja kirjutan selle kõik void setup osasse--kommenteerige miks ma nii teen.*/ Serial.println("Palun kommenteerige:"); Serial.println(IntegerMuutuja); Serial.println(UjukomaarvMuutuja); Serial.println(UjukomaarvMuutuja, 0); Serial.println(UjukomaarvMuutuja, 1); Serial.println(UjukomaarvMuutuja, 9); Serial.println(UjukomaarvMuutuja, 3); Serial.println(UjukomaarvMuutuja, 4); Serial.println(UjukomaarvMuutuja, 5); Serial.println(symbolTavaline); Serial.println(symbolTavalineArray); Serial.println(eestiKeelesArduino); Serial.println(Question); Serial.println("Kirjuta siia ise midagi--katsetage!"); Serial.println('a'); Serial.println("r"); } void loop() { /*Kui ei oska kommenteerida, tõstke kõik Serial-println käsud void loopi ja käivitage... ;) */ }
Ja kirjutage kommentaare, mängige läbi kombinatsioone. Ja esitage õpetajale küsimusi! Las tal olla ka põnev!
13.3.3. Tingimusdirektiiv
- if
if abil tehakse kindlaks, kas mingi tingimus on saabunud.
Näiteks:
if (Muutuja > 50) { // tegevuse kirjeldus }
Harjutus F
Leidke varasematest tundidest if-abil tingimuse saabumist kontrolliv programm.
Kommenteerige seda järgmise mustri järgi:
- kui if-tingimus … , mis on antud sulgudes, on tõene, siis täidetakse tegevuse kirjeldus loogeliste sulgude vahel. Kui aga if-tingimus ei ole tõene, siis programm liigub edasi … juurde ja ei täideta seda, mis loogelistes sulgudes.
if-tingimuse juurde käib võrdlemine:
- x == y (x puhul esineb samasus y-ga)
- x != y (x puhul ei esine samasust y-ga)
- x < y (x on väiksem kui y)
- x > y (x on suurem kui y)
- x <= y (x on väiksem kui või võrdne y-ga)
- x >= y (x on suurem kui või võrdne y-ga)
NB!
Kui kirjutame ühe võrdusmärgi, siis Arduino IDE süntaks mõistab, et anname x-le väärtuseks y. Samasust tähistatakse aga kahe võrdusmärgiga: == .
Väärtuse andmine (inglise keeles assignment) : Näiteks kui x = y ja y = 1, siis see ei tähenda võrdlemist—see tähendab muutuja x-le väärtuseks 1 andmist. Seda aga ei saa testida, sest see matemaatiline lause ongi alati vaid tõene: ise andsime muutuja x-le väärtuse ju! (Ja tõesed on kõik arvud samamoodi, millega me muutuja x-le väärtuse anname.
Samasus-operaator (inglise keeles comparison operator) aga on x==1 ning selle eesmärk on testida muutuja x-i ses osas, kas tal esineb samasus 1-ga või ei esine seda samasust—ja siis vastavalt programmi tekstile reageerida.
- if/else
if/else meetod aga annab suurema kontrolli koodiga kaetud tegevuste üle—võimaldab grupeerida erinevaid tegevusi:
Näiteks, kui sisend oli vähem kui 10 või võrdne 10-ga, siis üks tegevus. Kui sisend oli rohkem kui 10, siis teine:
if (sensoriData <=10) { // tegevus üks } else { // tegevus kaks }
else saab ka kasutada järgneva if ees:
if (sensoriData <= 10) { // tegevus üks } else if (sensoriData > 10) { // tegevus kaks } else { // tegevus kolm }
13.3.3. for-tsükkel
Uurisime for-tsüklit neljandas peatükis. Kasutasime for-tsüklit selleks, et kindel arv kordi mingit tegevust täidetaks. (Eestikeelsetes õppematerjalides nimetatakse kolmikpäisega for-tsüklit üldtsüklidirektiiviks.)
Süntaks näeb välja selline:
for (eeltegevused; jätkamistingimus; sammu järeltegevused) { //statement(s); }
Näiteks järgmises, 14. peatükis on pandud void setup () osasse for-avaldus: et signaliseerida, et meie skeem on edukalt käima läinud, vilgutab ta meie kirjutatud void setup () osas üheksa korda LED-i, siis järgneb edasi juba järgmine programmiosa. Vt järgnev näide programmilõigust:
for (int i = 1; i<= 9; i= i+1) /* Eeltegevus: anname muutuja i-le algväärtuseks 1. Jätkamistingimus: jätkame tsükliga seni, kuni oleme jõudnud üheksandat korda allpool loogelistes sulgudes toodud tegevuse lõpetamiseni—st i on jõudnud kasvada üheksani. Kui aga jõuab üle üheksa, siis on jätkamistingimus täitmata ja rohkem tegevuse täitmisi seega ei järgne. Sammu järeltegevus: iga sellise sammu järel anname i-le väärtuse, mis on eelmisest väärtusest ühe võrra suurem. */ { digitalWrite (LED, HIGH); delay (200); digitalWrite (LED, LOW); delay (400); }
13.3.5. while-tsükkel
while-tsükkel toimib seni, kuni avaldis sulgudes muutub vääraks. Seega kui midagi ei muutu, siis see tsükkel jääbki korduma.
Saame kasutada seda tsüklit signaali ootamiseks. Vt Serial monitorilt signaali andmine Arduinole—st seni kuni ei ole tulnud signaali näiteks LED helendama panna, midagi ei tehtagi—võime seda siinkohal tinglikult nimetada ka sündmuse ootamise tsükliks.
Süntaks:
while(expression){ // statement(s) } Näide: Muutuja = 0; while(Muutuja < 10){ // tee midagi Muutuja ++; }
13.4. Märgid ; ning {}
- ; (semikoolon)
Märgib programmis tehtud avalduse lõppu—kindlasti olete unustanud seda lisada ja märganud, et ilma seda lisamata programm ei kompileeru.
Harjutus G
Käivitage suvaline programm oma programmide kollektsioonist nii, et olete eelnevalt ära võtnud ühe semikooloni. Lugege veateadet. Jälgige, millise rea teeb IDE punaseks? Kas selle rea, kus asus viga? Katsetage.
- {} (loogelised sulud)
"{" peab millalgi enda järgi saama ka "}". Selleks, et oleks programmeerimisel parem jälgida, et igal algaval loogelisel sulul pikema programmiteksti järel on olemas ka lõppevast loogelisest sulust kaaslane, on Arduino IDE-l olemas võimalus klikkida kursoriga esimesel loogelisel sulul—sel juhul on näha, et seda programmiosa lõpetava loogelise sulu ümber tekib seda loogelist sulgu esile tõstev kastike. Katsetage!
Parim praktika programmi kirjutamisel on aga ise kohe iga loogelise sulu avamise järel ka kirja saada lõpetav loogeline sulg.
Harjutus H
Võtke mõnelt oma programmilt ära mõni loogeline sulg ja katsuge käivitada. Mida märkate. Lugege veateadet.
Mis saab siis, kui eksida ja panna if/else kontekstis loogeline sulg kogemata paar rida allapoole… Uurige. Lugege veateateid ja arutage koos teiste õpilastega.
13.3. 6. Kommentaaride kirjutamine
Kommentaare on kahte tüüpi:
- // ….
Näiteks
MuutjaX = 5; // Üherealine kommentaar: kui osa sellest peaks sattuma järgmisele reale, siis see võib põhjustada mittekompileerumise. Näiteks kui seesama pikk jutt siit kopeerida IDE-sse, siis on suur tõenäosus, et nii ka läheb…
Mille poolest aga on hea kasutada //-varianti:
kui on vaja mõne programmirea toimimine mingi katsetuse ajal peatada, ent seda rida kustutada ei soovi, siis // rea ees muudavad selle Arduinole loetamatuks—saame programmi katsetada ilma seda rida täitmata.
- /* … */
Näiteks
MuutjaX = 5; /* mitmerealine kommentaar: Siin saab teha ühe märkuse; Sellele lisada järgmise; Veel midagi olulist välja tuua. Ja ei ole ohtu, et sellest lõigust midagigi satuks häirima programmi tööd. */
Võrdle //-variandiga eespool toodud näite kopeerimise osas. Kumba kommentaaristiili eelistame? Mõlemal on omad head omadused.
13.3.7. Viikude olekutest pinMode; INPUT, OUTPUT; HIHG, LOW.
Kui viigu tähistamiseks loome muutuja, siis anname veel enne void setup () osa sellele väärtuse—viigunumbri. Viigud jagunesid Analog-in ning Digital-viikudeks, viimased oli võimalik jagada kahte gruppi—pulsilaiusmodulatsiooni võimekusega (Arduinol tähistatud ˷ loogelise kriipsukesega) ja ilma selle võimekuseta viikudeks.
void setup () osas anname pinMode() abil viigule rolli—kas sisendit hankida näiteks mingilt sensorilt (INPUT) või olla väljund-viiguks, näiteks mingi LED vilkumist juhtida (OUTPUT).
void loop () osas (ent võimalik ka void setup() osas—vt 14. tunni materjalidest) me asume digitalWrite() abil juhtima viigu tegevust käsklustega HIGH ja LOW. Meenutame, et pulsilaiusmodulatsiooni-viikudele saame anda korralduse numbrilisel kujul.
13.3.7.1. digitalWrite(viigunumber)
Toome süntaksinäite:
int rohelineLED = 3; void setup() { pinMode(rohelineLED, OUTPUT); } void loop() { digitalWrite(rohelineLED, HIGH); delay(1000); digitalWrite(rohelineLED, LOW); delay(1000); }
NB!
Ei tee paha meenutada, et oleme maininud lühistamise ohtu. Et seda isegi mitte kogemata ei juhtuks, siis alati veendume, et juhtmeid ümber tõestes on Arduino ühendus arvutiga katkestatud. Sama kehtib ka arvutist sõltumatu vooluallika kasutamise korral—on vana tõde, et elektritööde ajaks tuleb objekt välja lülitada.
13.3.7.2. digitalRead(viigunumber)
- kasutame seda digitaalsignaali lugemiseks (et teada saada, kas viik on HIGH või on LOW).
Süntaksinäide:
int rohelineLED = 3; // LED ühendatud viigule 3 int lyliti = 2; // surunupp-lüliti tarvis int Muutuja = 0; //muutuja, et digitlRead abil loetut salvestada void setup() { pinMode(rohelineLED, OUTPUT); // LED on väljundiks pinMode(lyliti, INPUT); // surunupp-lüliti on sisendiks } void loop() { Muutuja = digitalRead(lyliti); // loeme lülitiga ühenduses oleva viigu pinget digitalWrite(rohelineLED, Muutuja); // Muutujasse salvestatud väärtusega juhime viigule number 3 ühendatud LED-i. }
NB!
Kui meie näites muutuja lyliti—st viik number 2 ei ole ühendatud, siis võib digitalRead anda nii sisendit HIGH kui ka LOW… NB! Kui defineerime mingi viigu, siis tuleb seda viiku ka kasutada, sellele viigule ehitatud ühendused peavad korralikult toimima. Kui viiku enam ei vajata, siis tuleb see ka programmist eemaldada. (Sellisel juhul on hea võimalus lihtsalt rea algusesse panna //--igaks juhuks on programmirea tekst meil alles, samas aga mikrokontroller seda ei saa kasutada.)
13.3.7.3. analogRead(viigunumber)
Loeb vastava Analog-in viigu pinget.
Arduinol on analoog-digitaal konverter, mis vastestab 0-5V pinged täisarvulisteks (integer) väärtustega vahemikus 0 kuni 1023. See annab ühiku-resolutsiooniks: 5 V / 1024 = 0.0049V (4.9 millivolti). Niiviisi on võimalik üsna täpselt juhtida näiteks LED-i helendamist vastavalt fototakistilt loetavale väärtusele (vt 14. peatükk).
NB!
Analog-in viigu programmis esinemise korral—see peab olema ühendatud. Kui Analog-in viik ei ole ühendatud, siis peab viide sellele olema programmist kas kustutatud või tehtud mikrokontrolleri tarvis programmist mitte välja loetavaks (st välja kommenteeritud // abil). Kui jätame programmi sisse viite ANALOG IN tähistusega viigule nii, et seda tegelikult ei esine, siis võib juhtuda, et viigult loetakse midagi siis, kui me seda ei soovi.
Süntaksinäide:
int analoogViik = A3; int Muutuja = 0; void setup() { Serial.begin(9600); } void loop() { Muutuja = analogRead(analoogViik); Serial.println(Muutuja); }
13.3.7.4. analogWrite(viigunumber, väärtus)
Annab analoog-muutuja väärtuse (pulsilaiusmodulatsiooni abil) PMW tähistusega viigule. Kasutame seda järgmises peatükis fototakistilt loetava signaali abil LED juhtimiseks.
NB! kui ühendate vastava viigu valesti—näiteks mitte-PWM tähistusega viigule—siis see viik ei ole võimeline töötama pulsilaiusmodulatsiooni-signaaliga.
Süntaksinäide:
int rohelineLED = 11; // vaadake järgi, kas see on PWM võimekusega viik! int analoogViik = A0; //loeme signaali int Muutuja = 0; //Muutujasse salvestame A0 pealt loetud väärtused int MuutujaPealeMapFunktsiooni = 0; /*teeme ka teise muutuja, millesse vastestatud (map()) signaal */ void setup() { pinMode(rohelineLED, OUTPUT); } void loop() { Muutuja = analogRead(analoogViik); //loeme näidu A0 pealt MuutujaPealeMapFunktsiooni = map(Muutuja, 0, 1023, 0, 255); /*teeme map() abil analoogist PWM signaali digitaal-viigu 11 juhtimiseks: analogRead väärtused vahemikus 0-1023, analogWrite väärtused aga 0-255.*/ analogWrite(rohelineLED, MuutujaPealeMapFunktsiooni);// ja juhime map() järel saadud väärtustega digitaal-viiku, millel pulsilaiusmodulatsiooni-võimekus. }
13.3.8. map(Muutuja , madalAnalog, kõrgeAnalog, madalPWM, kõrgePWM)
- map() on hästi arusaadav meetod analoogsignaalist digitaalse pulsilaiusmodulatsioon-signaali arvutamiseks. Tõime selle kohta ülalpool näite, vaata praktilist näidet ka 14. peatüki materjalist.
13.3.9. Kui on vaja liita, lahutada, korrutada, jagadaNB!'
Näited
vastusVaartus = vaartusA + vaartusB; vastusVaartus = vaartusA - vaartusB; vastusVaartus = vaartusA * vaartusB; vastusVaartus = vaartusA / vaartusB;
NB!
Kui jagate Arduino IDE-s täisarvu (int) 9 täisarvuga (int) 2, siis saate vastuseks täisarvu (int) 4.
Miks?
Kui oleme defineerinud väärtused kui int, siis neid ei jagata nii, nagu jagataks siis, kui oleksime defineerinud tüübiga float (ujukoma-arv). Selleks, et saada komadega avastust 5/2= 2.5—selleks on vaja, et vähemalt üks tehtes osalevatest arvudest oleks tüübiga float. Jälgige seejuures ka arvutuste tulemuse salvestamiseks kasutatava muutuja tüüpi.
Harjutus I
Mängige järgneva programmiga ja tehke kindlaks, mis juhtudel juhtub nii, et saate ettekavatsematud vastused. Tehke sellest kirjalikke märkmeid ja võrrelge oma märkmeid kaasõpilase märkmetega. Vajadusel esitage õpetajale küsimusi.
float vaartus; int tulemus; void setup() { Serial.begin(9600); vaartus = 5 / 2.0; Serial.println (vaartus); } void loop() { vaartus = 9 / 2; Serial.println (vaartus); tulemus = 9.0 / 2.0; Serial.println (tulemus); delay(10000); }
13.3.10. Serial monitoriga kasutamiseks:
- Serial.begin()
Alustab void setup () osas Serial monitori. Ühtlasi annab ette ka baudid. Kasutada tuleb programmis neid baud rate määrasid, mida leiame Serial monitori aknast—allservas paremal pool.
Süntaksinäide:
void setup() { Serial.begin(9600); } void loop() {}
- Serial.available()
Kasutasime seda selleks, et USART asünkroonset sidet ning ASCII numbrilist toimimist illustreerida—vt peatükk 8 programminäidet.
Süntaksinäide:
int Muutuja = 0; // sissetulev serial-signaal void setup() { Serial.begin(9600); // teeb lahti serial porti, määrab 9600 baud rate. } void loop() { if (Serial.available() > 0) {//rakendub vaid siis, kui tuleb data incomingByte = Serial.read();// loeb sissetuleva väärtuse Serial.println(Muutuja); //prindib Muutuja Serial monitori ekraanile } }
- Serial.read()
Funktsioon tagastab esimese baidi sissetulevast data-st või -1 kui datat ei ole.
Süntaksinäide:
int Muutuja = 0; void setup() { Serial.begin(9600); } void loop() { if (Serial.available() > 0) { Muutuja = Serial.read(); Serial.println(Muutuja); } }
- Serial.write()
Eelistame Serial.write() funktsioonile Serial.print() ja Serial.println() funktsioone—kuna see funktsioon saadab data serial porti binaarse numbri abil.
Süntaksinäide:
void setup() { Serial.begin(9600); } void loop() { Serial.write(2); // saadab baitides Serial.write(’a’); //saadab }
- Serial.print() ning Serial.println()
Prindib data Serial porti—näeme Serial monitori abiga infot inimesele loetaval kujul (tähed, numbrid, etc). Saame lisada ka lauseid, lauseosi, vajalikke tähiseid.
NB!
Serial monitori kasutades ei saa kasutada digitaalviikudele 0 ja 1 (RX ja TX) ühendamist.
Mis juhtub, kui kasutada digitaalviikusid 0 ja 1 Serial monitoriga samal ajal?
Selles valikkursuses me ei ühenda midagi ei RX ega TX viigu külge.